@kaelio/ktx 0.13.1 → 0.15.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 (261) hide show
  1. package/assets/python/{kaelio_ktx-0.13.1-py3-none-any.whl → kaelio_ktx-0.15.0-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/cli-program.js +1 -1
  5. package/dist/commands/ingest-commands.d.ts +9 -0
  6. package/dist/commands/ingest-commands.js +37 -1
  7. package/dist/commands/knowledge-commands.js +3 -0
  8. package/dist/commands/setup-commands.js +15 -1
  9. package/dist/connection-drivers.d.ts +2 -0
  10. package/dist/connection-drivers.js +7 -3
  11. package/dist/connection.d.ts +3 -0
  12. package/dist/connection.js +27 -0
  13. package/dist/connectors/bigquery/connector.d.ts +17 -6
  14. package/dist/connectors/bigquery/connector.js +152 -76
  15. package/dist/connectors/bigquery/dialect.d.ts +2 -2
  16. package/dist/connectors/clickhouse/connector.d.ts +1 -0
  17. package/dist/connectors/clickhouse/connector.js +45 -16
  18. package/dist/connectors/clickhouse/dialect.d.ts +2 -2
  19. package/dist/connectors/mongodb/connector.d.ts +69 -0
  20. package/dist/connectors/mongodb/connector.js +307 -0
  21. package/dist/connectors/mongodb/dialect.d.ts +17 -0
  22. package/dist/connectors/mongodb/dialect.js +50 -0
  23. package/dist/connectors/mongodb/live-database-introspection.d.ts +10 -0
  24. package/dist/connectors/mongodb/live-database-introspection.js +24 -0
  25. package/dist/connectors/mongodb/schema-inference.d.ts +20 -0
  26. package/dist/connectors/mongodb/schema-inference.js +110 -0
  27. package/dist/connectors/mysql/connector.d.ts +1 -0
  28. package/dist/connectors/mysql/connector.js +18 -2
  29. package/dist/connectors/mysql/dialect.d.ts +2 -2
  30. package/dist/connectors/postgres/connector.d.ts +1 -0
  31. package/dist/connectors/postgres/connector.js +20 -3
  32. package/dist/connectors/postgres/dialect.d.ts +2 -2
  33. package/dist/connectors/snowflake/connector.d.ts +1 -0
  34. package/dist/connectors/snowflake/connector.js +27 -3
  35. package/dist/connectors/snowflake/dialect.d.ts +2 -2
  36. package/dist/connectors/sqlite/connector.d.ts +11 -1
  37. package/dist/connectors/sqlite/connector.js +120 -17
  38. package/dist/connectors/sqlite/dialect.d.ts +2 -2
  39. package/dist/connectors/sqlite/read-query-child.d.ts +1 -0
  40. package/dist/connectors/sqlite/read-query-child.js +23 -0
  41. package/dist/connectors/sqlserver/connector.d.ts +2 -0
  42. package/dist/connectors/sqlserver/connector.js +20 -3
  43. package/dist/connectors/sqlserver/dialect.d.ts +2 -2
  44. package/dist/context/cache/content-result-cache.d.ts +36 -0
  45. package/dist/context/cache/content-result-cache.js +14 -0
  46. package/dist/context/cache/sqlite-content-result-cache.d.ts +18 -0
  47. package/dist/context/cache/sqlite-content-result-cache.js +223 -0
  48. package/dist/context/connections/bigquery-identifiers.d.ts +1 -0
  49. package/dist/context/connections/bigquery-identifiers.js +7 -0
  50. package/dist/context/connections/configured-connections.d.ts +9 -0
  51. package/dist/context/connections/configured-connections.js +18 -0
  52. package/dist/context/connections/dialects.d.ts +30 -4
  53. package/dist/context/connections/dialects.js +38 -11
  54. package/dist/context/connections/drivers.js +21 -0
  55. package/dist/context/connections/gdrive-config.d.ts +19 -0
  56. package/dist/context/connections/gdrive-config.js +57 -0
  57. package/dist/context/connections/query-deadline.d.ts +31 -0
  58. package/dist/context/connections/query-deadline.js +37 -0
  59. package/dist/context/ingest/adapters/gdrive/chunk.d.ts +3 -0
  60. package/dist/context/ingest/adapters/gdrive/chunk.js +74 -0
  61. package/dist/context/ingest/adapters/gdrive/detect.d.ts +1 -0
  62. package/dist/context/ingest/adapters/gdrive/detect.js +20 -0
  63. package/dist/context/ingest/adapters/gdrive/fetch.d.ts +6 -0
  64. package/dist/context/ingest/adapters/gdrive/fetch.js +95 -0
  65. package/dist/context/ingest/adapters/gdrive/gdrive-client.d.ts +30 -0
  66. package/dist/context/ingest/adapters/gdrive/gdrive-client.js +132 -0
  67. package/dist/context/ingest/adapters/gdrive/gdrive.adapter.d.ts +11 -0
  68. package/dist/context/ingest/adapters/gdrive/gdrive.adapter.js +27 -0
  69. package/dist/context/ingest/adapters/gdrive/normalize.d.ts +2 -0
  70. package/dist/context/ingest/adapters/gdrive/normalize.js +256 -0
  71. package/dist/context/ingest/adapters/gdrive/types.d.ts +153 -0
  72. package/dist/context/ingest/adapters/gdrive/types.js +36 -0
  73. package/dist/context/ingest/adapters/live-database/daemon-introspection.js +24 -0
  74. package/dist/context/ingest/adapters/live-database/fetch-report.d.ts +8 -0
  75. package/dist/context/ingest/adapters/live-database/fetch-report.js +37 -0
  76. package/dist/context/ingest/adapters/live-database/live-database.adapter.d.ts +2 -1
  77. package/dist/context/ingest/adapters/live-database/live-database.adapter.js +9 -2
  78. package/dist/context/ingest/adapters/live-database/manifest.d.ts +2 -0
  79. package/dist/context/ingest/adapters/live-database/manifest.js +8 -4
  80. package/dist/context/ingest/adapters/live-database/scan-outcome.d.ts +17 -0
  81. package/dist/context/ingest/adapters/live-database/scan-outcome.js +40 -0
  82. package/dist/context/ingest/adapters/live-database/stage.js +5 -5
  83. package/dist/context/ingest/adapters/metabase/types.d.ts +1 -1
  84. package/dist/context/ingest/adapters/metabase/types.js +1 -1
  85. package/dist/context/ingest/adapters/sigma/chunk.d.ts +6 -0
  86. package/dist/context/ingest/adapters/sigma/chunk.js +119 -0
  87. package/dist/context/ingest/adapters/sigma/client-port.d.ts +45 -0
  88. package/dist/context/ingest/adapters/sigma/client-port.js +1 -0
  89. package/dist/context/ingest/adapters/sigma/client.d.ts +33 -0
  90. package/dist/context/ingest/adapters/sigma/client.js +176 -0
  91. package/dist/context/ingest/adapters/sigma/detect.d.ts +1 -0
  92. package/dist/context/ingest/adapters/sigma/detect.js +23 -0
  93. package/dist/context/ingest/adapters/sigma/fetch.d.ts +14 -0
  94. package/dist/context/ingest/adapters/sigma/fetch.js +191 -0
  95. package/dist/context/ingest/adapters/sigma/local-sigma.adapter.d.ts +17 -0
  96. package/dist/context/ingest/adapters/sigma/local-sigma.adapter.js +41 -0
  97. package/dist/context/ingest/adapters/sigma/project.d.ts +8 -0
  98. package/dist/context/ingest/adapters/sigma/project.js +186 -0
  99. package/dist/context/ingest/adapters/sigma/sigma.adapter.d.ts +18 -0
  100. package/dist/context/ingest/adapters/sigma/sigma.adapter.js +43 -0
  101. package/dist/context/ingest/adapters/sigma/types.d.ts +80 -0
  102. package/dist/context/ingest/adapters/sigma/types.js +82 -0
  103. package/dist/context/ingest/artifact-gates.d.ts +32 -1
  104. package/dist/context/ingest/artifact-gates.js +85 -18
  105. package/dist/context/ingest/final-gate-prune.d.ts +35 -0
  106. package/dist/context/ingest/final-gate-prune.js +229 -0
  107. package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
  108. package/dist/context/ingest/ingest-bundle.runner.js +459 -240
  109. package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +0 -11
  110. package/dist/context/ingest/isolated-diff/patch-integrator.js +0 -44
  111. package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
  112. package/dist/context/ingest/isolated-diff/work-unit-executor.js +13 -4
  113. package/dist/context/ingest/local-adapters.d.ts +2 -1
  114. package/dist/context/ingest/local-adapters.js +28 -0
  115. package/dist/context/ingest/local-bundle-runtime.js +7 -2
  116. package/dist/context/ingest/local-ingest.js +1 -1
  117. package/dist/context/ingest/local-stage-ingest.d.ts +3 -1
  118. package/dist/context/ingest/local-stage-ingest.js +3 -1
  119. package/dist/context/ingest/ports.d.ts +3 -0
  120. package/dist/context/ingest/report-snapshot.js +13 -3
  121. package/dist/context/ingest/reports.d.ts +3 -3
  122. package/dist/context/ingest/reports.js +3 -1
  123. package/dist/context/ingest/stages/build-wu-context.d.ts +1 -0
  124. package/dist/context/ingest/stages/build-wu-context.js +2 -1
  125. package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -1
  126. package/dist/context/ingest/stages/stage-3-work-units.js +4 -10
  127. package/dist/context/ingest/stages/validate-wu-sources.d.ts +12 -0
  128. package/dist/context/ingest/stages/validate-wu-sources.js +23 -8
  129. package/dist/context/ingest/tools/read-raw-file.tool.js +10 -5
  130. package/dist/context/ingest/tools/read-raw-span.tool.js +10 -5
  131. package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +1 -1
  132. package/dist/context/ingest/tools/warehouse-verification/entity-details.tool.js +1 -1
  133. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +1 -1
  134. package/dist/context/ingest/types.d.ts +3 -0
  135. package/dist/context/ingest/wiki-body-refs.d.ts +28 -0
  136. package/dist/context/ingest/wiki-body-refs.js +37 -8
  137. package/dist/context/ingest/wiki-sl-ref-repair.d.ts +1 -0
  138. package/dist/context/ingest/wiki-sl-ref-repair.js +4 -0
  139. package/dist/context/ingest/work-unit-cache.d.ts +67 -0
  140. package/dist/context/ingest/work-unit-cache.js +230 -0
  141. package/dist/context/llm/ai-sdk-runtime.d.ts +1 -0
  142. package/dist/context/llm/ai-sdk-runtime.js +5 -0
  143. package/dist/context/llm/claude-code-runtime.d.ts +4 -1
  144. package/dist/context/llm/claude-code-runtime.js +38 -8
  145. package/dist/context/llm/codex-runtime.d.ts +4 -1
  146. package/dist/context/llm/codex-runtime.js +48 -35
  147. package/dist/context/llm/runtime-port.d.ts +26 -0
  148. package/dist/context/llm/subprocess-generate-object-child.d.ts +1 -0
  149. package/dist/context/llm/subprocess-generate-object-child.js +34 -0
  150. package/dist/context/llm/subprocess-generate-object.d.ts +42 -0
  151. package/dist/context/llm/subprocess-generate-object.js +122 -0
  152. package/dist/context/mcp/context-tools.d.ts +2 -0
  153. package/dist/context/mcp/context-tools.js +111 -17
  154. package/dist/context/mcp/local-project-ports.d.ts +7 -0
  155. package/dist/context/mcp/local-project-ports.js +29 -0
  156. package/dist/context/mcp/logger.d.ts +24 -0
  157. package/dist/context/mcp/logger.js +49 -0
  158. package/dist/context/mcp/server.js +2 -0
  159. package/dist/context/mcp/types.d.ts +17 -0
  160. package/dist/context/memory/memory-agent.service.js +1 -1
  161. package/dist/context/project/config.d.ts +72 -0
  162. package/dist/context/project/config.js +5 -0
  163. package/dist/context/project/driver-schemas.d.ts +35 -0
  164. package/dist/context/project/driver-schemas.js +89 -1
  165. package/dist/context/project/project.js +1 -1
  166. package/dist/context/project/setup-config.js +1 -0
  167. package/dist/context/scan/description-generation.d.ts +4 -0
  168. package/dist/context/scan/description-generation.js +100 -13
  169. package/dist/context/scan/enabled-tables.d.ts +5 -0
  170. package/dist/context/scan/enabled-tables.js +13 -1
  171. package/dist/context/scan/enrichment-state.d.ts +50 -7
  172. package/dist/context/scan/enrichment-state.js +30 -13
  173. package/dist/context/scan/local-enrichment-artifacts.d.ts +41 -0
  174. package/dist/context/scan/local-enrichment-artifacts.js +155 -32
  175. package/dist/context/scan/local-enrichment.d.ts +39 -4
  176. package/dist/context/scan/local-enrichment.js +345 -92
  177. package/dist/context/scan/local-scan.d.ts +4 -1
  178. package/dist/context/scan/local-scan.js +54 -13
  179. package/dist/context/scan/local-structural-artifacts.d.ts +3 -1
  180. package/dist/context/scan/local-structural-artifacts.js +5 -0
  181. package/dist/context/scan/object-introspection.d.ts +21 -0
  182. package/dist/context/scan/object-introspection.js +35 -0
  183. package/dist/context/scan/relationship-benchmarks.js +5 -3
  184. package/dist/context/scan/relationship-composite-candidates.d.ts +6 -3
  185. package/dist/context/scan/relationship-composite-candidates.js +13 -1
  186. package/dist/context/scan/relationship-detection-budget.d.ts +35 -0
  187. package/dist/context/scan/relationship-detection-budget.js +53 -0
  188. package/dist/context/scan/relationship-diagnostics.d.ts +5 -0
  189. package/dist/context/scan/relationship-diagnostics.js +2 -0
  190. package/dist/context/scan/relationship-discovery.d.ts +9 -3
  191. package/dist/context/scan/relationship-discovery.js +27 -4
  192. package/dist/context/scan/relationship-llm-proposal.js +36 -27
  193. package/dist/context/scan/relationship-profiling.d.ts +7 -3
  194. package/dist/context/scan/relationship-profiling.js +53 -41
  195. package/dist/context/scan/relationship-validation.d.ts +6 -4
  196. package/dist/context/scan/relationship-validation.js +46 -32
  197. package/dist/context/scan/sqlite-local-enrichment-state-store.d.ts +8 -1
  198. package/dist/context/scan/sqlite-local-enrichment-state-store.js +71 -159
  199. package/dist/context/scan/types.d.ts +5 -3
  200. package/dist/context/scan/types.js +2 -0
  201. package/dist/context/sl/local-query.js +5 -0
  202. package/dist/context/sl/pglite-sl-search-prototype.js +1 -1
  203. package/dist/context/sl/semantic-layer.service.js +1 -1
  204. package/dist/context/sl/source-files.d.ts +5 -0
  205. package/dist/context/sl/source-files.js +17 -11
  206. package/dist/context/sl/tools/connection-id-schema.js +1 -1
  207. package/dist/context/sql-analysis/dialect-notes.d.ts +9 -0
  208. package/dist/context/sql-analysis/dialect-notes.js +40 -0
  209. package/dist/context/sql-analysis/dialects/bigquery.md +13 -0
  210. package/dist/context/sql-analysis/dialects/clickhouse.md +9 -0
  211. package/dist/context/sql-analysis/dialects/mysql.md +9 -0
  212. package/dist/context/sql-analysis/dialects/postgres.md +10 -0
  213. package/dist/context/sql-analysis/dialects/snowflake.md +10 -0
  214. package/dist/context/sql-analysis/dialects/sqlite.md +11 -0
  215. package/dist/context/sql-analysis/dialects/tsql.md +10 -0
  216. package/dist/context/wiki/keys.js +1 -1
  217. package/dist/context/wiki/knowledge-wiki.service.d.ts +4 -4
  218. package/dist/context/wiki/knowledge-wiki.service.js +2 -1
  219. package/dist/context/wiki/local-knowledge.d.ts +13 -0
  220. package/dist/context/wiki/local-knowledge.js +50 -6
  221. package/dist/context/wiki/sqlite-knowledge-index.d.ts +2 -0
  222. package/dist/context/wiki/sqlite-knowledge-index.js +21 -5
  223. package/dist/context/wiki/tools/wiki-write.tool.d.ts +2 -0
  224. package/dist/context/wiki/tools/wiki-write.tool.js +27 -0
  225. package/dist/context/wiki/types.d.ts +6 -0
  226. package/dist/context-build-view.d.ts +3 -0
  227. package/dist/context-build-view.js +1 -0
  228. package/dist/knowledge.d.ts +2 -0
  229. package/dist/knowledge.js +12 -1
  230. package/dist/local-adapters.js +11 -0
  231. package/dist/mcp-http-server.js +15 -1
  232. package/dist/mcp-server-factory.d.ts +2 -0
  233. package/dist/mcp-server-factory.js +15 -1
  234. package/dist/mcp-stdio-server.js +10 -2
  235. package/dist/notion-page-picker.js +1 -1
  236. package/dist/prompts/memory_agent_external_ingest.md +2 -0
  237. package/dist/public-ingest.d.ts +3 -1
  238. package/dist/public-ingest.js +4 -0
  239. package/dist/scan.d.ts +3 -1
  240. package/dist/scan.js +7 -0
  241. package/dist/setup-databases.d.ts +1 -1
  242. package/dist/setup-databases.js +43 -1
  243. package/dist/setup-sources.d.ts +6 -1
  244. package/dist/setup-sources.js +208 -48
  245. package/dist/setup.d.ts +3 -0
  246. package/dist/setup.js +6 -1
  247. package/dist/skills/analytics/SKILL.md +200 -2
  248. package/dist/skills/gdrive_synthesize/SKILL.md +97 -0
  249. package/dist/skills/sigma_ingest/SKILL.md +189 -0
  250. package/dist/skills/wiki_capture/SKILL.md +24 -0
  251. package/dist/sql.js +4 -0
  252. package/dist/status-project.d.ts +4 -0
  253. package/dist/status-project.js +54 -2
  254. package/dist/telemetry/events.d.ts +2 -2
  255. package/dist/text-ingest.d.ts +4 -0
  256. package/dist/text-ingest.js +58 -29
  257. package/dist/verbatim-ingest.d.ts +46 -0
  258. package/dist/verbatim-ingest.js +193 -0
  259. package/package.json +5 -1
  260. package/dist/context/ingest/final-gate-repair.d.ts +0 -28
  261. package/dist/context/ingest/final-gate-repair.js +0 -91
@@ -0,0 +1,186 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { sigmaProjectionConfigSchema, stagedDataModelFileSchema, STAGED_FILES } from './types.js';
5
+ async function readProjectionConfig(stagedDir) {
6
+ try {
7
+ const body = await readFile(join(stagedDir, STAGED_FILES.projectionConfig), 'utf-8');
8
+ return sigmaProjectionConfigSchema.parse(JSON.parse(body)).connectionMappings;
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ const SIGMA_AUTHOR = { name: 'Sigma', email: 'system@kaelio.dev' };
15
+ // Best-effort schema for the raw spec blob stored in staged data model files.
16
+ const warehouseTableSourceSchema = z.object({
17
+ kind: z.literal('warehouse-table'),
18
+ connectionId: z.string(),
19
+ path: z.array(z.string()),
20
+ });
21
+ const specColumnSchema = z
22
+ .object({
23
+ id: z.string(),
24
+ formula: z.string().optional(),
25
+ name: z.string().optional(),
26
+ hidden: z.boolean().optional(),
27
+ description: z.string().optional(),
28
+ format: z.object({ kind: z.string() }).passthrough().optional(),
29
+ })
30
+ .passthrough();
31
+ const specElementSchema = z
32
+ .object({
33
+ id: z.string(),
34
+ kind: z.string().optional(),
35
+ name: z.string().optional(),
36
+ hidden: z.boolean().optional(),
37
+ source: z.object({ kind: z.string() }).passthrough().optional(),
38
+ columns: z.array(specColumnSchema).optional(),
39
+ })
40
+ .passthrough();
41
+ const specPageSchema = z
42
+ .object({
43
+ id: z.string(),
44
+ name: z.string().optional(),
45
+ elements: z.array(specElementSchema).optional(),
46
+ })
47
+ .passthrough();
48
+ const sigmaSpecSchema = z
49
+ .object({
50
+ name: z.string().optional(),
51
+ pages: z.array(specPageSchema).optional(),
52
+ })
53
+ .passthrough();
54
+ /** Extract the column name from a bracket formula like `[TABLE/Column Name]` or `[Column]`. */
55
+ function extractColumnName(formula) {
56
+ const match = /\[(?:[^\]/]+\/)?([^\]]+)\]/.exec(formula.trim());
57
+ return match?.[1] ?? null;
58
+ }
59
+ function slugify(value) {
60
+ return value
61
+ .toLowerCase()
62
+ .replace(/[^a-z0-9]+/g, '_')
63
+ .replace(/^_+|_+$/g, '');
64
+ }
65
+ function inferColumnType(col) {
66
+ const kind = col.format?.kind;
67
+ if (kind === 'datetime' || kind === 'date')
68
+ return 'time';
69
+ if (kind === 'number' || kind === 'currency' || kind === 'percent')
70
+ return 'number';
71
+ return 'string';
72
+ }
73
+ function buildSourceFromElement(dataModelName, elementName, elementId, warehousePath, columns) {
74
+ const table = warehousePath.join('.');
75
+ if (!table)
76
+ return null;
77
+ const modelSlug = slugify(dataModelName || elementId);
78
+ const elemSlug = elementName ? slugify(elementName) : '';
79
+ const sourceName = elemSlug && elemSlug !== modelSlug ? `${modelSlug}_${elemSlug}` : modelSlug;
80
+ if (!sourceName)
81
+ return null;
82
+ const slColumns = [];
83
+ for (const col of columns) {
84
+ if (col.hidden)
85
+ continue;
86
+ if (!col.formula)
87
+ continue;
88
+ // Aggregation formulas (Sum, Count, etc.) are Sigma-specific expressions that don't map to
89
+ // warehouse columns — skip them silently. The sigma_ingest skill surfaces them as wiki candidates.
90
+ if (/^[A-Za-z]+\(/.test(col.formula.trim()))
91
+ continue;
92
+ const displayName = col.name ?? extractColumnName(col.formula);
93
+ if (!displayName)
94
+ continue;
95
+ const colSlug = slugify(displayName);
96
+ if (!colSlug)
97
+ continue;
98
+ slColumns.push({
99
+ name: colSlug,
100
+ type: inferColumnType(col),
101
+ ...(col.description ? { descriptions: { user: col.description } } : {}),
102
+ });
103
+ }
104
+ const source = {
105
+ name: sourceName,
106
+ table,
107
+ grain: [],
108
+ columns: slColumns,
109
+ joins: [],
110
+ measures: [],
111
+ };
112
+ if (dataModelName) {
113
+ source.descriptions = { user: dataModelName };
114
+ }
115
+ return source;
116
+ }
117
+ /** @internal */
118
+ export async function projectSigmaDataModels(ctx, slService) {
119
+ const svc = ctx.workdir ? slService.forWorktree(ctx.workdir) : slService;
120
+ const warnings = [];
121
+ const errors = [];
122
+ const touchedSources = [];
123
+ const connectionMappings = await readProjectionConfig(ctx.stagedDir);
124
+ const dmDir = join(ctx.stagedDir, STAGED_FILES.dataModelsDir);
125
+ let entries;
126
+ try {
127
+ entries = await readdir(dmDir);
128
+ }
129
+ catch {
130
+ return { warnings, errors, touchedSources, changedWikiPageKeys: [] };
131
+ }
132
+ for (const entry of entries) {
133
+ if (!entry.endsWith('.json'))
134
+ continue;
135
+ let stagedFile;
136
+ try {
137
+ const body = await readFile(join(dmDir, entry), 'utf-8');
138
+ stagedFile = stagedDataModelFileSchema.parse(JSON.parse(body));
139
+ }
140
+ catch {
141
+ warnings.push(`Skipping malformed staged file: ${entry}`);
142
+ continue;
143
+ }
144
+ if (!stagedFile.spec)
145
+ continue;
146
+ let spec;
147
+ try {
148
+ spec = sigmaSpecSchema.parse(stagedFile.spec);
149
+ }
150
+ catch {
151
+ warnings.push(`Skipping unparseable spec for data model "${stagedFile.name}"`);
152
+ continue;
153
+ }
154
+ for (const page of spec.pages ?? []) {
155
+ for (const element of page.elements ?? []) {
156
+ if (element.hidden)
157
+ continue;
158
+ const warehouseSource = warehouseTableSourceSchema.safeParse(element.source);
159
+ if (!warehouseSource.success)
160
+ continue;
161
+ const source = buildSourceFromElement(stagedFile.name, element.name, element.id, warehouseSource.data.path, element.columns ?? []);
162
+ if (!source)
163
+ continue;
164
+ // Only write SL sources for elements whose Sigma connection is mapped to a warehouse connection.
165
+ // Writing under an unmapped connection produces gate failures because the Sigma connection
166
+ // is not a warehouse connection and cannot be validated.
167
+ const targetConnectionId = connectionMappings[warehouseSource.data.connectionId];
168
+ if (!targetConnectionId) {
169
+ warnings.push(`Skipping SL source for "${stagedFile.name}" / "${element.name ?? element.id}": ` +
170
+ `no connectionMappings entry for Sigma connection ${warehouseSource.data.connectionId}. ` +
171
+ `Add a connectionMappings entry in ktx.yaml to enable SL projection for this element.`);
172
+ continue;
173
+ }
174
+ try {
175
+ const result = await svc.writeSource(targetConnectionId, source, SIGMA_AUTHOR.name, SIGMA_AUTHOR.email, `Sigma: import data model "${stagedFile.name}"`);
176
+ touchedSources.push({ connectionId: targetConnectionId, sourceName: source.name });
177
+ warnings.push(...result.warnings);
178
+ }
179
+ catch (err) {
180
+ errors.push(`Failed to write source "${source.name}": ${err instanceof Error ? err.message : String(err)}`);
181
+ }
182
+ }
183
+ }
184
+ }
185
+ return { warnings, errors, touchedSources, changedWikiPageKeys: [] };
186
+ }
@@ -0,0 +1,18 @@
1
+ import type { ChunkResult, DeterministicProjectionContext, DiffSet, FetchContext, ProjectionResult, SourceAdapter } from '../../types.js';
2
+ import type { SigmaClientFactory } from './client-port.js';
3
+ import { type SigmaFetchLogger } from './fetch.js';
4
+ export interface SigmaSourceAdapterDeps {
5
+ clientFactory: SigmaClientFactory;
6
+ logger?: SigmaFetchLogger;
7
+ }
8
+ export declare class SigmaSourceAdapter implements SourceAdapter {
9
+ private readonly deps;
10
+ readonly source = "sigma";
11
+ readonly skillNames: string[];
12
+ constructor(deps: SigmaSourceAdapterDeps);
13
+ detect(stagedDir: string): Promise<boolean>;
14
+ fetch(pullConfig: unknown, stagedDir: string, ctx: FetchContext): Promise<void>;
15
+ chunk(stagedDir: string, diffSet?: DiffSet): Promise<ChunkResult>;
16
+ listTargetConnectionIds(stagedDir: string): Promise<string[]>;
17
+ project(ctx: DeterministicProjectionContext): Promise<ProjectionResult>;
18
+ }
@@ -0,0 +1,43 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { chunkSigmaStagedDir } from './chunk.js';
4
+ import { detectSigmaStagedDir } from './detect.js';
5
+ import { fetchSigmaBundle } from './fetch.js';
6
+ import { projectSigmaDataModels } from './project.js';
7
+ import { sigmaProjectionConfigSchema, STAGED_FILES } from './types.js';
8
+ export class SigmaSourceAdapter {
9
+ deps;
10
+ source = 'sigma';
11
+ skillNames = ['sigma_ingest'];
12
+ constructor(deps) {
13
+ this.deps = deps;
14
+ }
15
+ detect(stagedDir) {
16
+ return detectSigmaStagedDir(stagedDir);
17
+ }
18
+ async fetch(pullConfig, stagedDir, ctx) {
19
+ await fetchSigmaBundle({
20
+ pullConfig,
21
+ stagedDir,
22
+ ctx,
23
+ clientFactory: this.deps.clientFactory,
24
+ ...(this.deps.logger ? { logger: this.deps.logger } : {}),
25
+ });
26
+ }
27
+ chunk(stagedDir, diffSet) {
28
+ return chunkSigmaStagedDir(stagedDir, { diffSet });
29
+ }
30
+ async listTargetConnectionIds(stagedDir) {
31
+ try {
32
+ const body = await readFile(join(stagedDir, STAGED_FILES.projectionConfig), 'utf-8');
33
+ const config = sigmaProjectionConfigSchema.parse(JSON.parse(body));
34
+ return [...new Set(Object.values(config.connectionMappings))].sort();
35
+ }
36
+ catch {
37
+ return [];
38
+ }
39
+ }
40
+ project(ctx) {
41
+ return projectSigmaDataModels(ctx, ctx.semanticLayerService);
42
+ }
43
+ }
@@ -0,0 +1,80 @@
1
+ import { z } from 'zod';
2
+ /** Filters applied when listing workbooks. Shared with ListWorkbooksOptions in client-port.ts. */
3
+ declare const workbookFilterSchema: z.ZodObject<{
4
+ includeArchived: z.ZodDefault<z.ZodBoolean>;
5
+ includeExplorations: z.ZodDefault<z.ZodBoolean>;
6
+ updatedSince: z.ZodOptional<z.ZodString>;
7
+ }, z.core.$strip>;
8
+ /** Input shape for listWorkbooks — all fields optional since the client applies its own defaults. */
9
+ export type WorkbookFilterInput = z.input<typeof workbookFilterSchema>;
10
+ /** The lean config the adapter needs at `fetch()` time, stored in the ingest job's `bundleRef.config`. */
11
+ declare const sigmaPullConfigSchema: z.ZodObject<{
12
+ sigmaConnectionId: z.ZodString;
13
+ connectionMappings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
14
+ workbookFilter: z.ZodDefault<z.ZodObject<{
15
+ includeArchived: z.ZodDefault<z.ZodBoolean>;
16
+ includeExplorations: z.ZodDefault<z.ZodBoolean>;
17
+ updatedSince: z.ZodOptional<z.ZodString>;
18
+ }, z.core.$strip>>;
19
+ dataModelFilter: z.ZodOptional<z.ZodObject<{
20
+ updatedSince: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strip>>;
22
+ }, z.core.$strip>;
23
+ export type SigmaPullConfig = z.infer<typeof sigmaPullConfigSchema>;
24
+ export declare function parseSigmaPullConfig(raw: unknown): SigmaPullConfig;
25
+ /** Written to stagedDir during fetch() and read back by project(), listTargetConnectionIds(), and the sigma_ingest skill. */
26
+ export declare const sigmaProjectionConfigSchema: z.ZodObject<{
27
+ connectionMappings: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
28
+ workbookFilter: z.ZodDefault<z.ZodObject<{
29
+ includeArchived: z.ZodDefault<z.ZodBoolean>;
30
+ includeExplorations: z.ZodDefault<z.ZodBoolean>;
31
+ updatedSince: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strip>>;
33
+ }, z.core.$strip>;
34
+ export type SigmaProjectionConfig = z.infer<typeof sigmaProjectionConfigSchema>;
35
+ /**
36
+ * A staged data model file, one per `data-models/<id>.json`.
37
+ * Stores the summary metadata plus the raw spec blob from GET /v2/dataModels/{id}/spec.
38
+ */
39
+ export declare const stagedDataModelFileSchema: z.ZodObject<{
40
+ sigmaId: z.ZodString;
41
+ name: z.ZodString;
42
+ path: z.ZodString;
43
+ latestVersion: z.ZodNumber;
44
+ updatedAt: z.ZodString;
45
+ isArchived: z.ZodDefault<z.ZodBoolean>;
46
+ dataModelUrlId: z.ZodOptional<z.ZodString>;
47
+ spec: z.ZodUnknown;
48
+ }, z.core.$strip>;
49
+ export type StagedDataModelFile = z.infer<typeof stagedDataModelFileSchema>;
50
+ /** The manifest written once per `fetch()`. Presence acts as the detect() sentinel. */
51
+ export declare const sigmaManifestSchema: z.ZodObject<{
52
+ sigmaConnectionId: z.ZodString;
53
+ fetchedAt: z.ZodString;
54
+ dataModelCount: z.ZodNumber;
55
+ workbookCount: z.ZodDefault<z.ZodNumber>;
56
+ }, z.core.$strip>;
57
+ export type SigmaManifest = z.infer<typeof sigmaManifestSchema>;
58
+ /**
59
+ * A staged workbook file, one per `workbooks/<id>.json`.
60
+ * Stores the summary metadata from GET /v2/workbooks (no separate spec endpoint).
61
+ */
62
+ export declare const stagedWorkbookFileSchema: z.ZodObject<{
63
+ sigmaId: z.ZodString;
64
+ name: z.ZodString;
65
+ path: z.ZodString;
66
+ latestVersion: z.ZodNumber;
67
+ updatedAt: z.ZodString;
68
+ isArchived: z.ZodDefault<z.ZodBoolean>;
69
+ workbookUrlId: z.ZodOptional<z.ZodString>;
70
+ description: z.ZodOptional<z.ZodString>;
71
+ }, z.core.$strip>;
72
+ export type StagedWorkbookFile = z.infer<typeof stagedWorkbookFileSchema>;
73
+ /** Filenames inside stagedDir. Centralized so chunk() + fetch() + detect() all agree. */
74
+ export declare const STAGED_FILES: {
75
+ readonly manifest: "sigma-manifest.json";
76
+ readonly projectionConfig: "sigma-projection-config.json";
77
+ readonly dataModelsDir: "data-models";
78
+ readonly workbooksDir: "workbooks";
79
+ };
80
+ export {};
@@ -0,0 +1,82 @@
1
+ import { z } from 'zod';
2
+ const sigmaLocalConnectionIdSchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/);
3
+ /** Filters applied when listing workbooks. Shared with ListWorkbooksOptions in client-port.ts. */
4
+ const workbookFilterSchema = z.object({
5
+ includeArchived: z.boolean().default(false),
6
+ includeExplorations: z.boolean().default(false),
7
+ /** ISO 8601 date string. Only workbooks updated on or after this date are included. */
8
+ updatedSince: z.string().optional(),
9
+ });
10
+ const dataModelFilterSchema = z.object({
11
+ /** ISO 8601 date string. Only data models updated on or after this date are fetched. */
12
+ updatedSince: z.string().optional(),
13
+ });
14
+ /** The lean config the adapter needs at `fetch()` time, stored in the ingest job's `bundleRef.config`. */
15
+ const sigmaPullConfigSchema = z.object({
16
+ /** The ktx connection ID for the Sigma instance being swept. */
17
+ sigmaConnectionId: sigmaLocalConnectionIdSchema,
18
+ /**
19
+ * Maps Sigma internal connection UUIDs (source.connectionId in data model specs)
20
+ * to ktx warehouse connection IDs. When present, projected semantic-layer sources
21
+ * are written under the mapped warehouse connection rather than the Sigma connection.
22
+ */
23
+ connectionMappings: z.record(z.string(), z.string()).optional(),
24
+ /** Filters applied when listing workbooks. Defaults exclude archived and exploration workbooks. */
25
+ workbookFilter: workbookFilterSchema.default({ includeArchived: false, includeExplorations: false }),
26
+ /** Filters applied when listing data models. */
27
+ dataModelFilter: dataModelFilterSchema.optional(),
28
+ });
29
+ export function parseSigmaPullConfig(raw) {
30
+ return sigmaPullConfigSchema.parse(raw);
31
+ }
32
+ /** Written to stagedDir during fetch() and read back by project(), listTargetConnectionIds(), and the sigma_ingest skill. */
33
+ export const sigmaProjectionConfigSchema = z.object({
34
+ connectionMappings: z.record(z.string(), z.string()).default({}),
35
+ /** Filters that were active when workbooks were last fetched. Tells the skill what the staged set covers. */
36
+ workbookFilter: workbookFilterSchema.default({ includeArchived: false, includeExplorations: false }),
37
+ });
38
+ /**
39
+ * A staged data model file, one per `data-models/<id>.json`.
40
+ * Stores the summary metadata plus the raw spec blob from GET /v2/dataModels/{id}/spec.
41
+ */
42
+ export const stagedDataModelFileSchema = z.object({
43
+ sigmaId: z.string(),
44
+ name: z.string(),
45
+ /** Full path in Sigma, e.g. "Finance/Revenue Model". */
46
+ path: z.string(),
47
+ latestVersion: z.number(),
48
+ updatedAt: z.string(),
49
+ isArchived: z.boolean().default(false),
50
+ /** URL-safe slug Sigma uses in the web UI (dataModelUrlId from the API). */
51
+ dataModelUrlId: z.string().optional(),
52
+ /** Raw spec from GET /v2/dataModels/{id}/spec (JSON format). */
53
+ spec: z.unknown(),
54
+ });
55
+ /** The manifest written once per `fetch()`. Presence acts as the detect() sentinel. */
56
+ export const sigmaManifestSchema = z.object({
57
+ sigmaConnectionId: sigmaLocalConnectionIdSchema,
58
+ fetchedAt: z.string(),
59
+ dataModelCount: z.number().int(),
60
+ workbookCount: z.number().int().default(0),
61
+ });
62
+ /**
63
+ * A staged workbook file, one per `workbooks/<id>.json`.
64
+ * Stores the summary metadata from GET /v2/workbooks (no separate spec endpoint).
65
+ */
66
+ export const stagedWorkbookFileSchema = z.object({
67
+ sigmaId: z.string(),
68
+ name: z.string(),
69
+ path: z.string(),
70
+ latestVersion: z.number(),
71
+ updatedAt: z.string(),
72
+ isArchived: z.boolean().default(false),
73
+ workbookUrlId: z.string().optional(),
74
+ description: z.string().optional(),
75
+ });
76
+ /** Filenames inside stagedDir. Centralized so chunk() + fetch() + detect() all agree. */
77
+ export const STAGED_FILES = {
78
+ manifest: 'sigma-manifest.json',
79
+ projectionConfig: 'sigma-projection-config.json',
80
+ dataModelsDir: 'data-models',
81
+ workbooksDir: 'workbooks',
82
+ };
@@ -2,6 +2,7 @@ import type { SemanticLayerService } from '../../context/sl/semantic-layer.servi
2
2
  import type { TouchedSlSource } from '../../context/tools/touched-sl-sources.js';
3
3
  import type { KnowledgeWikiService } from '../../context/wiki/knowledge-wiki.service.js';
4
4
  import type { WuValidationResult } from './stages/validate-wu-sources.js';
5
+ import { type WikiBodyRefIssue } from './wiki-body-refs.js';
5
6
  export interface FinalArtifactGateInput {
6
7
  connectionIds: string[];
7
8
  changedWikiPageKeys: string[];
@@ -18,5 +19,35 @@ export interface ProvenanceRawPathValidationInput {
18
19
  currentRawPaths: Set<string>;
19
20
  deletedRawPaths: Set<string>;
20
21
  }
21
- export declare function validateFinalIngestArtifacts(input: FinalArtifactGateInput): Promise<void>;
22
+ export type FinalArtifactGateFinding = {
23
+ kind: 'invalid_source';
24
+ connectionId: string;
25
+ sourceName: string;
26
+ errors: string[];
27
+ } | {
28
+ kind: 'missing_join_target';
29
+ ownerConnectionId: string;
30
+ ownerSourceName: string;
31
+ targetSourceName: string;
32
+ message: string;
33
+ } | {
34
+ kind: 'missing_wiki_ref';
35
+ pageKey: string;
36
+ targetPageKey: string;
37
+ message: string;
38
+ } | {
39
+ kind: 'missing_wiki_sl_ref';
40
+ pageKey: string;
41
+ ref: string;
42
+ sourceName: string;
43
+ entityName: string | null;
44
+ message: string;
45
+ } | WikiBodyRefIssue;
46
+ export interface FinalArtifactGateResult {
47
+ ok: boolean;
48
+ findings: FinalArtifactGateFinding[];
49
+ }
50
+ export declare function formatFinalArtifactGateFindings(findings: FinalArtifactGateFinding[]): string;
51
+ export declare function isFinalArtifactGateFindingPruneable(finding: FinalArtifactGateFinding): boolean;
52
+ export declare function validateFinalIngestArtifacts(input: FinalArtifactGateInput): Promise<FinalArtifactGateResult>;
22
53
  export declare function validateProvenanceRawPaths(input: ProvenanceRawPathValidationInput): void;
@@ -1,5 +1,8 @@
1
1
  import { findMissingWikiRefs } from '../wiki/wiki-ref-validation.js';
2
- import { findInvalidWikiBodyRefs } from './wiki-body-refs.js';
2
+ import { findInvalidWikiBodyRefIssues } from './wiki-body-refs.js';
3
+ function normalizeRawPath(path) {
4
+ return path.replace(/\\/g, '/').replace(/^\/+/, '');
5
+ }
3
6
  function parseSlRef(ref) {
4
7
  const withoutConnection = ref.includes('/') ? ref.slice(ref.indexOf('/') + 1) : ref;
5
8
  const connectionId = ref.includes('/') ? ref.slice(0, ref.indexOf('/')) : null;
@@ -14,7 +17,7 @@ function slEntityNames(source) {
14
17
  ]);
15
18
  }
16
19
  async function validateWikiSlRefs(input) {
17
- const errors = [];
20
+ const findings = [];
18
21
  const sourcesByConnection = new Map();
19
22
  for (const connectionId of input.connectionIds) {
20
23
  const { sources } = await input.semanticLayerService.loadAllSources(connectionId);
@@ -36,18 +39,32 @@ async function validateWikiSlRefs(input) {
36
39
  }
37
40
  }
38
41
  if (!source) {
39
- errors.push(`${pageKey}: unknown sl_refs entry ${ref}`);
42
+ findings.push({
43
+ kind: 'missing_wiki_sl_ref',
44
+ pageKey,
45
+ ref,
46
+ sourceName: parsed.sourceName,
47
+ entityName: parsed.entityName,
48
+ message: `${pageKey}: unknown sl_refs entry ${ref}`,
49
+ });
40
50
  continue;
41
51
  }
42
52
  if (parsed.entityName && !slEntityNames(source).has(parsed.entityName)) {
43
- errors.push(`${pageKey}: unknown sl_refs entity ${ref}`);
53
+ findings.push({
54
+ kind: 'missing_wiki_sl_ref',
55
+ pageKey,
56
+ ref,
57
+ sourceName: parsed.sourceName,
58
+ entityName: parsed.entityName,
59
+ message: `${pageKey}: unknown sl_refs entity ${ref}`,
60
+ });
44
61
  }
45
62
  }
46
63
  }
47
- return errors;
64
+ return findings;
48
65
  }
49
66
  async function validateWikiRefs(input) {
50
- const dangling = [];
67
+ const findings = [];
51
68
  for (const pageKey of input.changedWikiPageKeys) {
52
69
  const page = await input.wikiService.readPage('GLOBAL', null, pageKey);
53
70
  if (!page) {
@@ -62,28 +79,77 @@ async function validateWikiRefs(input) {
62
79
  content: page.content,
63
80
  });
64
81
  for (const missingRef of missingRefs) {
65
- dangling.push(`${pageKey} -> ${missingRef}`);
82
+ findings.push({
83
+ kind: 'missing_wiki_ref',
84
+ pageKey,
85
+ targetPageKey: missingRef,
86
+ message: `${pageKey} -> ${missingRef}`,
87
+ });
88
+ }
89
+ }
90
+ return findings;
91
+ }
92
+ export function formatFinalArtifactGateFindings(findings) {
93
+ const errors = findings.map((finding) => {
94
+ if (finding.kind === 'invalid_source') {
95
+ return `semantic-layer validation failed for ${finding.connectionId}:${finding.sourceName}: ${finding.errors.join('; ')}`;
96
+ }
97
+ if (finding.kind === 'missing_wiki_ref') {
98
+ return `wiki reference targets missing page: ${finding.message}`;
99
+ }
100
+ return finding.message;
101
+ });
102
+ return `final artifact gates failed:\n${errors.join('\n')}`;
103
+ }
104
+ export function isFinalArtifactGateFindingPruneable(finding) {
105
+ switch (finding.kind) {
106
+ case 'invalid_source':
107
+ case 'missing_join_target':
108
+ case 'missing_wiki_ref':
109
+ case 'missing_wiki_sl_ref':
110
+ case 'missing_wiki_body_sl_entity':
111
+ case 'missing_wiki_body_sl_source':
112
+ case 'missing_wiki_body_table':
113
+ return true;
114
+ default: {
115
+ const exhaustive = finding;
116
+ return exhaustive;
66
117
  }
67
118
  }
68
- return dangling;
69
119
  }
70
120
  export async function validateFinalIngestArtifacts(input) {
71
121
  // Join-neighbor expansion happens inside validateTouchedSources so work-unit
72
122
  // validation and this gate check the same set — a source that passes one
73
123
  // passes the other.
74
124
  const validation = await input.validateTouchedSources(input.touchedSlSources);
75
- const errors = validation.invalidSources.map((invalid) => `semantic-layer validation failed for ${invalid.source}: ${invalid.errors.join('; ')}`);
76
- errors.push(...(await validateWikiSlRefs(input)));
77
- const danglingWikiRefs = await validateWikiRefs(input);
78
- if (danglingWikiRefs.length > 0) {
79
- errors.push(`wiki references target missing page(s): ${danglingWikiRefs.join(', ')}`);
125
+ const findings = [];
126
+ for (const invalid of validation.invalidSources) {
127
+ const [connectionId = '', sourceName = ''] = invalid.source.split(':', 2);
128
+ const issues = invalid.issues ?? invalid.errors.map((message) => ({ kind: 'source_validation', message }));
129
+ const sourceErrors = issues.filter((issue) => issue.kind === 'source_validation').map((issue) => issue.message);
130
+ if (sourceErrors.length > 0) {
131
+ findings.push({ kind: 'invalid_source', connectionId, sourceName, errors: sourceErrors });
132
+ }
133
+ for (const issue of issues) {
134
+ if (issue.kind === 'missing_join_target') {
135
+ findings.push({
136
+ kind: 'missing_join_target',
137
+ ownerConnectionId: connectionId,
138
+ ownerSourceName: sourceName,
139
+ targetSourceName: issue.targetSourceName,
140
+ message: issue.message,
141
+ });
142
+ }
143
+ }
80
144
  }
145
+ findings.push(...(await validateWikiSlRefs(input)));
146
+ findings.push(...(await validateWikiRefs(input)));
81
147
  for (const pageKey of input.changedWikiPageKeys) {
82
148
  const page = await input.wikiService.readPage('GLOBAL', null, pageKey);
83
149
  if (!page) {
84
150
  continue;
85
151
  }
86
- errors.push(...(await findInvalidWikiBodyRefs({
152
+ findings.push(...(await findInvalidWikiBodyRefIssues({
87
153
  pageKey,
88
154
  body: page.content,
89
155
  visibleConnectionIds: input.connectionIds,
@@ -94,13 +160,14 @@ export async function validateFinalIngestArtifacts(input) {
94
160
  tableExists: input.tableExists,
95
161
  })));
96
162
  }
97
- if (errors.length > 0) {
98
- throw new Error(`final artifact gates failed:\n${errors.join('\n')}`);
99
- }
163
+ return { ok: findings.length === 0, findings };
100
164
  }
101
165
  export function validateProvenanceRawPaths(input) {
166
+ const currentRawPaths = new Set([...input.currentRawPaths].map(normalizeRawPath));
167
+ const deletedRawPaths = new Set([...input.deletedRawPaths].map(normalizeRawPath));
102
168
  for (const row of input.rows) {
103
- if (!input.currentRawPaths.has(row.rawPath) && !input.deletedRawPaths.has(row.rawPath)) {
169
+ const rawPath = normalizeRawPath(row.rawPath);
170
+ if (!currentRawPaths.has(rawPath) && !deletedRawPaths.has(rawPath)) {
104
171
  throw new Error(`provenance row references raw path outside this snapshot: ${row.rawPath}`);
105
172
  }
106
173
  }
@@ -0,0 +1,35 @@
1
+ import type { KtxFileStorePort } from '../core/file-store.js';
2
+ import type { KnowledgeWikiService } from '../wiki/knowledge-wiki.service.js';
3
+ import type { FinalArtifactGateFinding } from './artifact-gates.js';
4
+ import type { IngestTraceWriter } from './ingest-trace.js';
5
+ type FinalGatePrunedReferenceKind = 'join' | 'wiki_ref' | 'wiki_sl_ref' | 'wiki_body_ref';
6
+ type SemanticLayerFileStore = Pick<KtxFileStorePort, 'readFile' | 'writeFile' | 'deleteFile' | 'listFiles'>;
7
+ export interface FinalGatePrunedReference {
8
+ kind: FinalGatePrunedReferenceKind;
9
+ artifact: string;
10
+ removedRef: string;
11
+ absentTarget: string;
12
+ }
13
+ export interface FinalGateDroppedSource {
14
+ connectionId: string;
15
+ sourceName: string;
16
+ reason: string;
17
+ }
18
+ export interface FinalGatePruneResult {
19
+ prunedReferences: FinalGatePrunedReference[];
20
+ droppedSources: FinalGateDroppedSource[];
21
+ }
22
+ interface PruneInput {
23
+ workdir: string;
24
+ semanticLayerFiles: SemanticLayerFileStore;
25
+ findings: FinalArtifactGateFinding[];
26
+ droppedSources: FinalGateDroppedSource[];
27
+ trace: IngestTraceWriter;
28
+ author: {
29
+ name: string;
30
+ email: string;
31
+ };
32
+ wikiService?: KnowledgeWikiService;
33
+ }
34
+ export declare function pruneFinalGateFindings(input: PruneInput): Promise<FinalGatePruneResult>;
35
+ export {};