kiri-cli 1.0.0b3__py3-none-any.whl

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.

Potentially problematic release.


This version of kiri-cli might be problematic. Click here for more details.

Files changed (743) hide show
  1. kiri_adapters/__init__.py +193 -0
  2. kiri_adapters/backends/__init__.py +0 -0
  3. kiri_adapters/backends/databricks.manifest.yml +55 -0
  4. kiri_adapters/backends/databricks.py +2980 -0
  5. kiri_adapters/backends/fabric_dw.manifest.yml +17 -0
  6. kiri_adapters/backends/fabric_dw.py +307 -0
  7. kiri_adapters/backends/fabric_lakehouse.manifest.yml +19 -0
  8. kiri_adapters/backends/fabric_lakehouse.py +365 -0
  9. kiri_adapters/backends/iceberg.manifest.yml +17 -0
  10. kiri_adapters/backends/iceberg.py +451 -0
  11. kiri_adapters/backends/localduckdb.manifest.yml +43 -0
  12. kiri_adapters/backends/localduckdb.py +1295 -0
  13. kiri_adapters/backends/mssql.manifest.yml +17 -0
  14. kiri_adapters/backends/mssql.py +366 -0
  15. kiri_adapters/backends/postgres.manifest.yml +17 -0
  16. kiri_adapters/backends/postgres.py +434 -0
  17. kiri_adapters/backends/trino.manifest.yml +17 -0
  18. kiri_adapters/backends/trino.py +542 -0
  19. kiri_adapters/base.py +892 -0
  20. kiri_adapters/doctor_checks.py +81 -0
  21. kiri_adapters/dv_planner.py +1205 -0
  22. kiri_adapters/dv_planner_bridge.py +265 -0
  23. kiri_adapters/dv_planner_pit.py +265 -0
  24. kiri_adapters/dv_planner_ref.py +403 -0
  25. kiri_adapters/dv_planner_sat_variants.py +668 -0
  26. kiri_adapters/framework.py +112 -0
  27. kiri_adapters/incident/__init__.py +30 -0
  28. kiri_adapters/incident/_http.py +121 -0
  29. kiri_adapters/incident/base.py +89 -0
  30. kiri_adapters/incident/jira.py +365 -0
  31. kiri_adapters/incident/servicenow.py +314 -0
  32. kiri_adapters/incident/zendesk.py +285 -0
  33. kiri_adapters/manifest.py +466 -0
  34. kiri_adapters/orchestration/__init__.py +30 -0
  35. kiri_adapters/orchestration/airflow.py +117 -0
  36. kiri_adapters/orchestration/artifact_emitter.py +99 -0
  37. kiri_adapters/orchestration/autoloader_emitter.py +141 -0
  38. kiri_adapters/orchestration/azure_data_factory.py +137 -0
  39. kiri_adapters/orchestration/base.py +626 -0
  40. kiri_adapters/orchestration/databricks_workflows.py +1768 -0
  41. kiri_adapters/orchestration/k8s_cron.py +555 -0
  42. kiri_adapters/orchestration/lifecycle.py +222 -0
  43. kiri_adapters/orchestration/pipeline_compat.py +145 -0
  44. kiri_adapters/orchestration/runtime_python.py +188 -0
  45. kiri_adapters/paths.py +38 -0
  46. kiri_adapters/protocols/__init__.py +64 -0
  47. kiri_adapters/protocols/ai_service/__init__.py +7 -0
  48. kiri_adapters/protocols/ai_service/conformance.py +120 -0
  49. kiri_adapters/protocols/base.py +220 -0
  50. kiri_adapters/protocols/catalog/__init__.py +7 -0
  51. kiri_adapters/protocols/catalog/conformance.py +138 -0
  52. kiri_adapters/protocols/framework/__init__.py +7 -0
  53. kiri_adapters/protocols/framework/conformance.py +98 -0
  54. kiri_adapters/protocols/incident/__init__.py +7 -0
  55. kiri_adapters/protocols/incident/conformance.py +64 -0
  56. kiri_adapters/protocols/orchestration/__init__.py +7 -0
  57. kiri_adapters/protocols/orchestration/conformance.py +84 -0
  58. kiri_adapters/protocols/platform/__init__.py +7 -0
  59. kiri_adapters/protocols/platform/conformance.py +120 -0
  60. kiri_adapters/protocols/sql_dialect/__init__.py +7 -0
  61. kiri_adapters/protocols/sql_dialect/conformance.py +91 -0
  62. kiri_adapters/protocols/vault/__init__.py +7 -0
  63. kiri_adapters/protocols/vault/conformance.py +65 -0
  64. kiri_adapters/py.typed +0 -0
  65. kiri_adapters/registry.py +104 -0
  66. kiri_adapters/semantic/__init__.py +60 -0
  67. kiri_adapters/semantic/apply.py +274 -0
  68. kiri_adapters/semantic/base.py +530 -0
  69. kiri_adapters/semantic/cube.py +280 -0
  70. kiri_adapters/semantic/dbt_semantic.py +92 -0
  71. kiri_adapters/semantic/metric_views.py +249 -0
  72. kiri_adapters/semantic/metricflow.py +345 -0
  73. kiri_adapters/semantic/snowflake_views.py +75 -0
  74. kiri_adapters/sql_dialect.py +1592 -0
  75. kiri_adapters/tsql_to_spark.py +653 -0
  76. kiri_ai_gateway/__init__.py +147 -0
  77. kiri_ai_gateway/audit.py +334 -0
  78. kiri_ai_gateway/batch.py +293 -0
  79. kiri_ai_gateway/config.py +165 -0
  80. kiri_ai_gateway/gateway.py +642 -0
  81. kiri_ai_gateway/placeholder_guard.py +145 -0
  82. kiri_ai_gateway/prices/__init__.py +77 -0
  83. kiri_ai_gateway/prices/prices.yaml +64 -0
  84. kiri_ai_gateway/prompts/__init__.py +54 -0
  85. kiri_ai_gateway/prompts/airbyte_source_setup.yaml +212 -0
  86. kiri_ai_gateway/prompts/bdv_translation.yaml +90 -0
  87. kiri_ai_gateway/prompts/contract_draft_mvp.yaml +117 -0
  88. kiri_ai_gateway/prompts/describe_table.yaml +19 -0
  89. kiri_ai_gateway/prompts/design_gold_from_question.yaml +130 -0
  90. kiri_ai_gateway/prompts/detect_pii.yaml +59 -0
  91. kiri_ai_gateway/prompts/discover_classify_column.yaml +69 -0
  92. kiri_ai_gateway/prompts/discover_describe_table.yaml +61 -0
  93. kiri_ai_gateway/prompts/dv_quality_summary.yaml +36 -0
  94. kiri_ai_gateway/prompts/incident_assist.yaml +58 -0
  95. kiri_ai_gateway/prompts/infer_source_schema.yaml +31 -0
  96. kiri_ai_gateway/prompts/ingest_backend_chooser.yaml +116 -0
  97. kiri_ai_gateway/prompts/interactive_source_edit.yaml +94 -0
  98. kiri_ai_gateway/prompts/map_needed_entity_to_source.yaml +72 -0
  99. kiri_ai_gateway/prompts/meet_kiri.yaml +127 -0
  100. kiri_ai_gateway/prompts/native_source_setup.yaml +211 -0
  101. kiri_ai_gateway/prompts/source_grouping_reason.yaml +29 -0
  102. kiri_ai_gateway/prompts/suggest_attribute_classification.yaml +42 -0
  103. kiri_ai_gateway/prompts/suggest_data_tests.yaml +71 -0
  104. kiri_ai_gateway/prompts/suggest_gold_dimension.yaml +108 -0
  105. kiri_ai_gateway/prompts/suggest_gold_fact.yaml +107 -0
  106. kiri_ai_gateway/prompts/suggest_gold_model.yaml +70 -0
  107. kiri_ai_gateway/prompts/suggest_semantic_intent.yaml +91 -0
  108. kiri_ai_gateway/prompts/suggest_silver_model.yaml +140 -0
  109. kiri_ai_gateway/providers/__init__.py +7 -0
  110. kiri_ai_gateway/providers/anthropic_provider.py +26 -0
  111. kiri_ai_gateway/providers/gateway_provider.py +188 -0
  112. kiri_ai_gateway/providers/local_provider.py +177 -0
  113. kiri_ai_gateway/py.typed +0 -0
  114. kiri_ai_gateway/service.py +283 -0
  115. kiri_ai_gateway/transport.py +103 -0
  116. kiri_ai_gateway/transports/__init__.py +12 -0
  117. kiri_ai_gateway/transports/anthropic_transport.py +73 -0
  118. kiri_ai_gateway/transports/ollama_transport.py +82 -0
  119. kiri_ai_gateway/transports/openai_compatible_transport.py +125 -0
  120. kiri_ai_gateway/workspace_policy.py +52 -0
  121. kiri_catalog/__init__.py +199 -0
  122. kiri_catalog/annotation_plan.py +183 -0
  123. kiri_catalog/appearances.py +155 -0
  124. kiri_catalog/backends/__init__.py +79 -0
  125. kiri_catalog/backends/base.py +167 -0
  126. kiri_catalog/backends/memory.py +241 -0
  127. kiri_catalog/backends/postgres.py +497 -0
  128. kiri_catalog/backends/redacting.py +195 -0
  129. kiri_catalog/backends/sqlite.py +480 -0
  130. kiri_catalog/context.py +94 -0
  131. kiri_catalog/federation/__init__.py +114 -0
  132. kiri_catalog/federation/api.py +200 -0
  133. kiri_catalog/federation/cache.py +149 -0
  134. kiri_catalog/federation/emit.py +209 -0
  135. kiri_catalog/federation/fs_resolver.py +295 -0
  136. kiri_catalog/federation/http_resolver.py +486 -0
  137. kiri_catalog/federation/metrics.py +123 -0
  138. kiri_catalog/federation/release.py +342 -0
  139. kiri_catalog/federation/resolver.py +332 -0
  140. kiri_catalog/federation/schema.py +460 -0
  141. kiri_catalog/importer.py +668 -0
  142. kiri_catalog/lineage_drift.py +280 -0
  143. kiri_catalog/lineage_graph.py +176 -0
  144. kiri_catalog/lineage_graph_explain.py +96 -0
  145. kiri_catalog/lineage_graph_mermaid.py +230 -0
  146. kiri_catalog/observed_uc_lineage.py +216 -0
  147. kiri_catalog/pii_detect.py +176 -0
  148. kiri_catalog/push.py +122 -0
  149. kiri_catalog/py.typed +0 -0
  150. kiri_catalog/redact.py +101 -0
  151. kiri_catalog/review_actions.py +119 -0
  152. kiri_catalog/schema/__init__.py +70 -0
  153. kiri_catalog/schema/asset.py +142 -0
  154. kiri_catalog/schema/column.py +247 -0
  155. kiri_catalog/schema/enums.py +195 -0
  156. kiri_catalog/schema/job.py +51 -0
  157. kiri_catalog/schema/lineage.py +33 -0
  158. kiri_catalog/schema/policy.py +69 -0
  159. kiri_catalog/schema/primitives.py +90 -0
  160. kiri_catalog/schema/review_state.py +111 -0
  161. kiri_catalog/schema/urn.py +45 -0
  162. kiri_catalog/schema_discovery.py +160 -0
  163. kiri_catalog/sinks/__init__.py +64 -0
  164. kiri_catalog/sinks/base.py +178 -0
  165. kiri_catalog/sinks/horizon.py +195 -0
  166. kiri_catalog/sinks/purview.py +216 -0
  167. kiri_catalog/sinks/unity.py +352 -0
  168. kiri_catalog/sinks/unity_executor.py +543 -0
  169. kiri_catalog/sources/__init__.py +42 -0
  170. kiri_catalog/sources/base.py +104 -0
  171. kiri_catalog/sources/unity.py +465 -0
  172. kiri_catalog/steward_writeback.py +158 -0
  173. kiri_catalog/tag_sql.py +99 -0
  174. kiri_cli/LICENSE +105 -0
  175. kiri_cli/__init__.py +1 -0
  176. kiri_cli/_apply_cli.py +63 -0
  177. kiri_cli/_apply_reporter.py +38 -0
  178. kiri_cli/_closed_menu.py +36 -0
  179. kiri_cli/_fixture_option.py +18 -0
  180. kiri_cli/_layer_detect.py +158 -0
  181. kiri_cli/_project_option.py +79 -0
  182. kiri_cli/adapter_cmd.py +192 -0
  183. kiri_cli/ai_batch_cmd.py +258 -0
  184. kiri_cli/ai_cmd.py +352 -0
  185. kiri_cli/approve_cmd.py +659 -0
  186. kiri_cli/attr_cmd.py +575 -0
  187. kiri_cli/audit_cmd.py +348 -0
  188. kiri_cli/audit_correlate_cmd.py +648 -0
  189. kiri_cli/audit_join.py +437 -0
  190. kiri_cli/audit_join_cmd.py +160 -0
  191. kiri_cli/auto_cmd.py +97 -0
  192. kiri_cli/azure_cmd.py +243 -0
  193. kiri_cli/catalog.py +1293 -0
  194. kiri_cli/cli_reference_live.py +147 -0
  195. kiri_cli/compile_cmd.py +493 -0
  196. kiri_cli/compliance_cmd.py +221 -0
  197. kiri_cli/contract_approval_cmd.py +94 -0
  198. kiri_cli/contract_cmd.py +64 -0
  199. kiri_cli/contract_codeowners_cmd.py +162 -0
  200. kiri_cli/contract_diff.py +111 -0
  201. kiri_cli/contract_emit.py +90 -0
  202. kiri_cli/contract_export.py +192 -0
  203. kiri_cli/contract_lint.py +1505 -0
  204. kiri_cli/contract_merge_cmd.py +264 -0
  205. kiri_cli/contract_new.py +326 -0
  206. kiri_cli/contract_promote.py +216 -0
  207. kiri_cli/contract_scaffold_cmd.py +134 -0
  208. kiri_cli/contract_schema_export.py +69 -0
  209. kiri_cli/contract_show.py +150 -0
  210. kiri_cli/contract_stubs.py +60 -0
  211. kiri_cli/contract_sync_from_live.py +327 -0
  212. kiri_cli/contract_validate.py +154 -0
  213. kiri_cli/contract_verify_live.py +242 -0
  214. kiri_cli/cost_cmd.py +243 -0
  215. kiri_cli/databricks_health.py +1254 -0
  216. kiri_cli/databricks_setup_cmd.py +712 -0
  217. kiri_cli/discover_cmd.py +600 -0
  218. kiri_cli/docs_cmd.py +35 -0
  219. kiri_cli/doctor_cmd.py +145 -0
  220. kiri_cli/dv_cmd.py +814 -0
  221. kiri_cli/dv_conformance_cmd.py +154 -0
  222. kiri_cli/dv_install.py +493 -0
  223. kiri_cli/dv_migrate_cmd.py +183 -0
  224. kiri_cli/dv_test_cmd.py +175 -0
  225. kiri_cli/federation_cmd.py +315 -0
  226. kiri_cli/fk_cmd.py +160 -0
  227. kiri_cli/flows_cmd.py +910 -0
  228. kiri_cli/governance_cmd.py +358 -0
  229. kiri_cli/incident_api_cmd.py +180 -0
  230. kiri_cli/incident_cmd.py +410 -0
  231. kiri_cli/incident_setup_cmd.py +239 -0
  232. kiri_cli/ingest.py +427 -0
  233. kiri_cli/ingest_discover.py +480 -0
  234. kiri_cli/ingest_new_source.py +386 -0
  235. kiri_cli/intent_cmd.py +122 -0
  236. kiri_cli/invariants_cmd.py +118 -0
  237. kiri_cli/kiri_banner.py +161 -0
  238. kiri_cli/kpi_cmd.py +222 -0
  239. kiri_cli/layer_cmd.py +464 -0
  240. kiri_cli/lineage.py +601 -0
  241. kiri_cli/main.py +2472 -0
  242. kiri_cli/maturity_cmd.py +222 -0
  243. kiri_cli/migrate_cmd.py +1347 -0
  244. kiri_cli/onboard_cmd.py +223 -0
  245. kiri_cli/pack_cmd.py +496 -0
  246. kiri_cli/pipeline_cmd.py +127 -0
  247. kiri_cli/pre_classify_cmd.py +274 -0
  248. kiri_cli/project_cmd.py +592 -0
  249. kiri_cli/py.typed +0 -0
  250. kiri_cli/quality_cmd.py +295 -0
  251. kiri_cli/rbac.py +139 -0
  252. kiri_cli/rbac_cmd.py +291 -0
  253. kiri_cli/release.py +850 -0
  254. kiri_cli/release_semver.py +265 -0
  255. kiri_cli/repo_cmd.py +155 -0
  256. kiri_cli/retention_cmd.py +333 -0
  257. kiri_cli/sample_data/__init__.py +12 -0
  258. kiri_cli/sample_data/customers.contract.yml +47 -0
  259. kiri_cli/sample_data/customers.csv +25 -0
  260. kiri_cli/scaffold_cmd.py +721 -0
  261. kiri_cli/schema_cmd.py +185 -0
  262. kiri_cli/semantic_cmd.py +605 -0
  263. kiri_cli/silver_cmd.py +348 -0
  264. kiri_cli/skills_cmd.py +243 -0
  265. kiri_cli/skills_data/README.md +51 -0
  266. kiri_cli/skills_data/add-source/SKILL.md +116 -0
  267. kiri_cli/skills_data/azure-deploy/SKILL.md +261 -0
  268. kiri_cli/skills_data/azure-status/SKILL.md +111 -0
  269. kiri_cli/skills_data/compliance-check/SKILL.md +99 -0
  270. kiri_cli/skills_data/databricks-build-pipeline/SKILL.md +112 -0
  271. kiri_cli/skills_data/databricks-project-setup/SKILL.md +157 -0
  272. kiri_cli/skills_data/debug-apply/SKILL.md +186 -0
  273. kiri_cli/skills_data/draft-gold-model/SKILL.md +203 -0
  274. kiri_cli/skills_data/explain-kpi/SKILL.md +158 -0
  275. kiri_cli/skills_data/incident-coach/SKILL.md +135 -0
  276. kiri_cli/skills_data/incident-setup/SKILL.md +112 -0
  277. kiri_cli/skills_data/maturity-coach/SKILL.md +120 -0
  278. kiri_cli/skills_data/promote-contract/SKILL.md +91 -0
  279. kiri_cli/skills_data/review-attributes/SKILL.md +94 -0
  280. kiri_cli/sla_cmd.py +769 -0
  281. kiri_cli/source_cmd.py +909 -0
  282. kiri_cli/templates/__init__.py +6 -0
  283. kiri_cli/templates/dv4dbt_packages.yml.j2 +11 -0
  284. kiri_cli/upgrade_cmd.py +896 -0
  285. kiri_cli/use_cmd.py +120 -0
  286. kiri_cli/vault_cmd.py +241 -0
  287. kiri_cli/verify_generated.py +241 -0
  288. kiri_cli-1.0.0b3.dist-info/METADATA +212 -0
  289. kiri_cli-1.0.0b3.dist-info/RECORD +743 -0
  290. kiri_cli-1.0.0b3.dist-info/WHEEL +4 -0
  291. kiri_cli-1.0.0b3.dist-info/entry_points.txt +4 -0
  292. kiri_compliance/__init__.py +33 -0
  293. kiri_compliance/audit_coverage.py +354 -0
  294. kiri_compliance/controls.py +763 -0
  295. kiri_compliance/evidence.py +155 -0
  296. kiri_compliance/py.typed +0 -0
  297. kiri_compliance/quality_engine.py +246 -0
  298. kiri_compliance/renderer.py +195 -0
  299. kiri_compliance/report.py +44 -0
  300. kiri_compliance/schema.py +129 -0
  301. kiri_core/__init__.py +185 -0
  302. kiri_core/adapter_surfaces.py +149 -0
  303. kiri_core/ai_query_gate/__init__.py +92 -0
  304. kiri_core/ai_query_gate/audit.py +153 -0
  305. kiri_core/ai_query_gate/evaluator.py +484 -0
  306. kiri_core/ai_query_gate/models.py +162 -0
  307. kiri_core/ai_query_gate/parser.py +436 -0
  308. kiri_core/ai_query_gate/resolver.py +274 -0
  309. kiri_core/ai_query_gate/store.py +234 -0
  310. kiri_core/ai_query_gate/store_postgres.py +409 -0
  311. kiri_core/api_keys.py +243 -0
  312. kiri_core/apply/__init__.py +27 -0
  313. kiri_core/apply/conformance.py +99 -0
  314. kiri_core/apply/incident_router.py +176 -0
  315. kiri_core/apply/models.py +140 -0
  316. kiri_core/apply/reporter.py +46 -0
  317. kiri_core/apply/runner.py +3083 -0
  318. kiri_core/apply_log_store.py +155 -0
  319. kiri_core/approval_flow/__init__.py +133 -0
  320. kiri_core/approval_flow/integration.py +156 -0
  321. kiri_core/approval_flow/models.py +443 -0
  322. kiri_core/approval_flow/notifier.py +263 -0
  323. kiri_core/approval_flow/orchestrator.py +660 -0
  324. kiri_core/approval_flow/reconcile_signoff.py +53 -0
  325. kiri_core/approval_flow/store.py +220 -0
  326. kiri_core/approval_flow/store_jsonl.py +217 -0
  327. kiri_core/approval_flow/store_postgres.py +580 -0
  328. kiri_core/approval_policy.py +116 -0
  329. kiri_core/asgi_trace.py +160 -0
  330. kiri_core/compliance.py +802 -0
  331. kiri_core/cutover_approval.py +187 -0
  332. kiri_core/discovery/__init__.py +109 -0
  333. kiri_core/discovery/ai/__init__.py +37 -0
  334. kiri_core/discovery/ai/_payload.py +115 -0
  335. kiri_core/discovery/ai/budget.py +83 -0
  336. kiri_core/discovery/ai/classify.py +223 -0
  337. kiri_core/discovery/ai/describe.py +200 -0
  338. kiri_core/discovery/ai/errors.py +49 -0
  339. kiri_core/discovery/discover.py +648 -0
  340. kiri_core/discovery/heuristics/__init__.py +42 -0
  341. kiri_core/discovery/heuristics/classification.py +174 -0
  342. kiri_core/discovery/heuristics/medallion.py +157 -0
  343. kiri_core/discovery/heuristics/pii.py +92 -0
  344. kiri_core/discovery/introspector.py +636 -0
  345. kiri_core/discovery/introspector_databricks.py +396 -0
  346. kiri_core/discovery/introspector_postgres.py +394 -0
  347. kiri_core/discovery/introspector_snowflake.py +383 -0
  348. kiri_core/discovery/reporting/__init__.py +17 -0
  349. kiri_core/discovery/reporting/discovery_report.py +190 -0
  350. kiri_core/discovery/scaffold/__init__.py +41 -0
  351. kiri_core/discovery/scaffold/contract_writer.py +632 -0
  352. kiri_core/discovery/scaffold/provenance_overlay.py +143 -0
  353. kiri_core/discovery/scaffold/source_writer.py +211 -0
  354. kiri_core/discovery/scaffold/todo_marker.py +98 -0
  355. kiri_core/domain_policy.py +122 -0
  356. kiri_core/goal_coverage.py +82 -0
  357. kiri_core/gold_registry.py +211 -0
  358. kiri_core/health.py +192 -0
  359. kiri_core/incident.py +204 -0
  360. kiri_core/incident_builders.py +256 -0
  361. kiri_core/incident_log.py +418 -0
  362. kiri_core/intent/__init__.py +46 -0
  363. kiri_core/intent/agent_runner.py +82 -0
  364. kiri_core/intent/project_dv.py +261 -0
  365. kiri_core/intent/propose.py +132 -0
  366. kiri_core/intent/semantic_intent.py +446 -0
  367. kiri_core/invariants.py +301 -0
  368. kiri_core/kpi.py +373 -0
  369. kiri_core/latency.py +150 -0
  370. kiri_core/lifecycle/__init__.py +555 -0
  371. kiri_core/lifecycle/ai_index.py +453 -0
  372. kiri_core/lifecycle/auto_mode.py +172 -0
  373. kiri_core/lifecycle/bootstrap.py +316 -0
  374. kiri_core/lifecycle/cli_reference.py +1064 -0
  375. kiri_core/lifecycle/commit.py +258 -0
  376. kiri_core/lifecycle/compile_dispatch.py +296 -0
  377. kiri_core/lifecycle/compile_range.py +665 -0
  378. kiri_core/lifecycle/contract_inventory.py +354 -0
  379. kiri_core/lifecycle/contracts.py +250 -0
  380. kiri_core/lifecycle/doctor.py +412 -0
  381. kiri_core/lifecycle/git_remote.py +948 -0
  382. kiri_core/lifecycle/identity.py +147 -0
  383. kiri_core/lifecycle/ingestion_modes.py +215 -0
  384. kiri_core/lifecycle/layer_contract.py +452 -0
  385. kiri_core/lifecycle/layer_contract_migrate.py +969 -0
  386. kiri_core/lifecycle/layer_contract_parser.py +436 -0
  387. kiri_core/lifecycle/layer_contract_validator.py +2291 -0
  388. kiri_core/lifecycle/layout.py +298 -0
  389. kiri_core/lifecycle/onboarding.py +306 -0
  390. kiri_core/lifecycle/openlineage_emit.py +153 -0
  391. kiri_core/lifecycle/patch.py +231 -0
  392. kiri_core/lifecycle/plan.py +241 -0
  393. kiri_core/lifecycle/probe.py +204 -0
  394. kiri_core/lifecycle/project_context.py +334 -0
  395. kiri_core/lifecycle/project_status.py +532 -0
  396. kiri_core/lifecycle/scaffold.py +455 -0
  397. kiri_core/lifecycle/source_grouping.py +725 -0
  398. kiri_core/lifecycle/source_introspection.py +488 -0
  399. kiri_core/lifecycle/source_inventory.py +561 -0
  400. kiri_core/lifecycle/source_patch.py +353 -0
  401. kiri_core/lifecycle/sources.py +470 -0
  402. kiri_core/lifecycle/sync_from_live.py +727 -0
  403. kiri_core/lifecycle/validate.py +116 -0
  404. kiri_core/lifecycle/verify_annotations.py +391 -0
  405. kiri_core/lifecycle/verify_live.py +389 -0
  406. kiri_core/lifecycle/verify_metric_views.py +321 -0
  407. kiri_core/lifecycle/watermark.py +371 -0
  408. kiri_core/lifecycle/watermark_apply.py +656 -0
  409. kiri_core/lineage.py +418 -0
  410. kiri_core/loader.py +377 -0
  411. kiri_core/logging.py +374 -0
  412. kiri_core/maturity.py +592 -0
  413. kiri_core/migration_layer_policy.py +285 -0
  414. kiri_core/names.py +163 -0
  415. kiri_core/observed_lineage.py +62 -0
  416. kiri_core/odcs/__init__.py +15 -0
  417. kiri_core/otel_bootstrap.py +326 -0
  418. kiri_core/pipeline_ir.py +332 -0
  419. kiri_core/pipeline_loader.py +82 -0
  420. kiri_core/planning.py +110 -0
  421. kiri_core/project.py +1576 -0
  422. kiri_core/project_docs.py +199 -0
  423. kiri_core/py.typed +0 -0
  424. kiri_core/rate_limit.py +456 -0
  425. kiri_core/release_state/__init__.py +37 -0
  426. kiri_core/release_state/models.py +108 -0
  427. kiri_core/release_state/store.py +634 -0
  428. kiri_core/resilience/__init__.py +288 -0
  429. kiri_core/resilience/retry.py +173 -0
  430. kiri_core/runtime.py +40 -0
  431. kiri_core/schema/__init__.py +62 -0
  432. kiri_core/schema/column.py +140 -0
  433. kiri_core/schema/ingestion.py +665 -0
  434. kiri_core/schema/model.py +44 -0
  435. kiri_core/schema/reporting_goal.py +160 -0
  436. kiri_core/schema/source.py +188 -0
  437. kiri_core/silver/__init__.py +148 -0
  438. kiri_core/silver/apply_runner.py +830 -0
  439. kiri_core/silver/backfill.py +280 -0
  440. kiri_core/silver/dv_backend.py +426 -0
  441. kiri_core/silver/dv_graph.py +932 -0
  442. kiri_core/silver/dv_graph_mermaid.py +443 -0
  443. kiri_core/silver/dv_graph_payload.py +79 -0
  444. kiri_core/silver/dv_graph_views.py +416 -0
  445. kiri_core/silver/graph_projector.py +566 -0
  446. kiri_core/silver/hash_canonicaliser.py +172 -0
  447. kiri_core/silver/hash_policy.py +195 -0
  448. kiri_core/silver/load_ts_policy.py +202 -0
  449. kiri_core/silver/schema_evolution.py +410 -0
  450. kiri_core/silver/zone_rules.py +81 -0
  451. kiri_core/sizing.py +134 -0
  452. kiri_core/storage_paths.py +130 -0
  453. kiri_core/technique_validator.py +700 -0
  454. kiri_core/trace.py +409 -0
  455. kiri_core/types.py +41 -0
  456. kiri_core/urn.py +161 -0
  457. kiri_dbt_bridge/__init__.py +22 -0
  458. kiri_dbt_bridge/_schema_resolve.py +38 -0
  459. kiri_dbt_bridge/bridge.py +94 -0
  460. kiri_dbt_bridge/dbt.manifest.yml +26 -0
  461. kiri_dbt_bridge/dv4dbt/__init__.py +143 -0
  462. kiri_dbt_bridge/dv4dbt/backend.py +204 -0
  463. kiri_dbt_bridge/dv4dbt/bridge_generator.py +289 -0
  464. kiri_dbt_bridge/dv4dbt/compat.py +232 -0
  465. kiri_dbt_bridge/dv4dbt/hub_generator.py +282 -0
  466. kiri_dbt_bridge/dv4dbt/link_generator.py +306 -0
  467. kiri_dbt_bridge/dv4dbt/manifest.py +203 -0
  468. kiri_dbt_bridge/dv4dbt/pit_generator.py +318 -0
  469. kiri_dbt_bridge/dv4dbt/policy_translation.py +249 -0
  470. kiri_dbt_bridge/dv4dbt/project_writer.py +588 -0
  471. kiri_dbt_bridge/dv4dbt/ref_generator.py +404 -0
  472. kiri_dbt_bridge/dv4dbt/render.py +180 -0
  473. kiri_dbt_bridge/dv4dbt/sat_generator.py +480 -0
  474. kiri_dbt_bridge/dv4dbt/sat_variant_generator.py +529 -0
  475. kiri_dbt_bridge/dv4dbt/templates/__init__.py +16 -0
  476. kiri_dbt_bridge/dv4dbt/templates/bridge.sql.j2 +9 -0
  477. kiri_dbt_bridge/dv4dbt/templates/dbt_project.yml.j2 +37 -0
  478. kiri_dbt_bridge/dv4dbt/templates/hub.sql.j2 +9 -0
  479. kiri_dbt_bridge/dv4dbt/templates/link.sql.j2 +9 -0
  480. kiri_dbt_bridge/dv4dbt/templates/packages.yml.j2 +10 -0
  481. kiri_dbt_bridge/dv4dbt/templates/pit.sql.j2 +9 -0
  482. kiri_dbt_bridge/dv4dbt/templates/ref_historised.sql.j2 +9 -0
  483. kiri_dbt_bridge/dv4dbt/templates/ref_non_historised.sql.j2 +9 -0
  484. kiri_dbt_bridge/dv4dbt/templates/sat.sql.j2 +9 -0
  485. kiri_dbt_bridge/dv4dbt/templates/sat_effectivity.sql.j2 +9 -0
  486. kiri_dbt_bridge/dv4dbt/templates/sat_multi_active.sql.j2 +9 -0
  487. kiri_dbt_bridge/dv4dbt/templates/sat_non_historised.sql.j2 +9 -0
  488. kiri_dbt_bridge/dv4dbt/templates/sat_record_tracking.sql.j2 +9 -0
  489. kiri_dbt_bridge/dv4dbt/templates/sat_virtual_end_dated.sql.j2 +9 -0
  490. kiri_dbt_bridge/dv4dbt/templates/sources.yml.j2 +39 -0
  491. kiri_dbt_bridge/framework_adapter.py +310 -0
  492. kiri_dbt_bridge/generate.py +372 -0
  493. kiri_dbt_bridge/gold/__init__.py +21 -0
  494. kiri_dbt_bridge/gold/_sql_emit.py +128 -0
  495. kiri_dbt_bridge/gold/assemble.py +90 -0
  496. kiri_dbt_bridge/gold/canonical_columns.py +168 -0
  497. kiri_dbt_bridge/gold/computed_expressions.py +179 -0
  498. kiri_dbt_bridge/gold/dialects/__init__.py +69 -0
  499. kiri_dbt_bridge/gold/dialects/databricks.py +150 -0
  500. kiri_dbt_bridge/gold/dialects/duckdb.py +120 -0
  501. kiri_dbt_bridge/gold/dialects/trino.py +136 -0
  502. kiri_dbt_bridge/gold/dim_generator.py +688 -0
  503. kiri_dbt_bridge/gold/dimension_refs.py +255 -0
  504. kiri_dbt_bridge/gold/dispatch.py +69 -0
  505. kiri_dbt_bridge/gold/fact_generator.py +534 -0
  506. kiri_dbt_bridge/gold/flat_generator.py +144 -0
  507. kiri_dbt_bridge/gold/lineage.py +484 -0
  508. kiri_dbt_bridge/gold/localized_twins.py +218 -0
  509. kiri_dbt_bridge/gold/measures.py +49 -0
  510. kiri_dbt_bridge/gold/role_playing.py +75 -0
  511. kiri_dbt_bridge/gold/surrogate_key.py +114 -0
  512. kiri_dbt_bridge/macros/kiri_scd1_dim.sql +57 -0
  513. kiri_dbt_bridge/macros/kiri_scd2_dim.sql +188 -0
  514. kiri_dbt_bridge/macros/kiri_sha256.sql +32 -0
  515. kiri_dbt_bridge/macros/kiri_string_cast.sql +37 -0
  516. kiri_dbt_bridge/macros/kiri_surrogate_key.sql +43 -0
  517. kiri_dbt_bridge/profiles.py +162 -0
  518. kiri_dbt_bridge/py.typed +0 -0
  519. kiri_dbt_bridge/safe_ident.py +44 -0
  520. kiri_dbt_bridge/silver_conformance.py +188 -0
  521. kiri_dbt_bridge/silver_kimball/__init__.py +27 -0
  522. kiri_dbt_bridge/silver_kimball/assemble.py +174 -0
  523. kiri_dbt_bridge/silver_kimball/dim_generator.py +602 -0
  524. kiri_dbt_bridge/silver_kimball/dispatch.py +86 -0
  525. kiri_dbt_bridge/silver_kimball/fact_generator.py +320 -0
  526. kiri_dbt_bridge/silver_kimball/flat_generator.py +156 -0
  527. kiri_dbt_bridge/silver_kimball/lineage.py +464 -0
  528. kiri_dbt_bridge/silver_kimball/watermark_window.py +199 -0
  529. kiri_dbt_bridge/silver_kimball/zone.py +68 -0
  530. kiri_dbt_bridge/silver_scd2_curated/__init__.py +46 -0
  531. kiri_dbt_bridge/silver_scd2_curated/assemble.py +213 -0
  532. kiri_dbt_bridge/silver_scd2_curated/curated_generator.py +351 -0
  533. kiri_dbt_bridge/silver_scd2_curated/standardized_generator.py +261 -0
  534. kiri_dbt_bridge/silver_scd2_curated/standardized_plan.py +254 -0
  535. kiri_iceberg/__init__.py +11 -0
  536. kiri_iceberg/catalog.py +128 -0
  537. kiri_iceberg/profile.py +131 -0
  538. kiri_iceberg/py.typed +0 -0
  539. kiri_ingestion/__init__.py +63 -0
  540. kiri_ingestion/airbyte_client.py +428 -0
  541. kiri_ingestion/database_fetcher.py +224 -0
  542. kiri_ingestion/layout.py +71 -0
  543. kiri_ingestion/mssql_introspect.py +565 -0
  544. kiri_ingestion/py.typed +0 -0
  545. kiri_ingestion/rest_fetcher.py +472 -0
  546. kiri_ingestion/runner.py +341 -0
  547. kiri_ingestion/schema_drift.py +222 -0
  548. kiri_ingestion/storage.py +621 -0
  549. kiri_mcp/__init__.py +5 -0
  550. kiri_mcp/audit.py +99 -0
  551. kiri_mcp/audit_decorators.py +252 -0
  552. kiri_mcp/main.py +22 -0
  553. kiri_mcp/py.typed +0 -0
  554. kiri_mcp/rbac.py +110 -0
  555. kiri_mcp/server.py +766 -0
  556. kiri_mcp/skills.py +297 -0
  557. kiri_mcp/tools/__init__.py +70 -0
  558. kiri_mcp/tools/_shared.py +58 -0
  559. kiri_mcp/tools/adapter.py +150 -0
  560. kiri_mcp/tools/cli_reference.py +59 -0
  561. kiri_mcp/tools/compliance.py +119 -0
  562. kiri_mcp/tools/contract.py +374 -0
  563. kiri_mcp/tools/dv.py +62 -0
  564. kiri_mcp/tools/federation.py +152 -0
  565. kiri_mcp/tools/intent.py +71 -0
  566. kiri_mcp/tools/lineage.py +204 -0
  567. kiri_mcp/tools/lineage_drift.py +75 -0
  568. kiri_mcp/tools/plan.py +66 -0
  569. kiri_mcp/tools/project.py +311 -0
  570. kiri_migration/__init__.py +56 -0
  571. kiri_migration/name_preservation.py +271 -0
  572. kiri_migration/predeploy.py +311 -0
  573. kiri_migration/py.typed +0 -0
  574. kiri_migration/reconcile.py +1891 -0
  575. kiri_migration/scaffold/__init__.py +90 -0
  576. kiri_migration/scaffold/_audit_columns.py +74 -0
  577. kiri_migration/scaffold/_classify.py +100 -0
  578. kiri_migration/scaffold/_typemap.py +71 -0
  579. kiri_migration/scaffold/_yaml.py +33 -0
  580. kiri_migration/scaffold/bronze.py +279 -0
  581. kiri_migration/scaffold/compile_targets/__init__.py +271 -0
  582. kiri_migration/scaffold/compile_targets/_common.py +193 -0
  583. kiri_migration/scaffold/compile_targets/copy_into.py +33 -0
  584. kiri_migration/scaffold/compile_targets/dlt.py +125 -0
  585. kiri_migration/scaffold/compile_targets/dlt_with_append_flows.py +163 -0
  586. kiri_migration/scaffold/compile_targets/dlt_with_apply_changes.py +192 -0
  587. kiri_migration/scaffold/compile_targets/dlt_with_expectations.py +276 -0
  588. kiri_migration/scaffold/compile_targets/layer_dispatch.py +258 -0
  589. kiri_migration/scaffold/compile_targets/sql_full_snapshot.py +385 -0
  590. kiri_migration/scaffold/compile_targets/streaming_table.py +37 -0
  591. kiri_migration/scaffold/mart.py +234 -0
  592. kiri_migration/scaffold/plans/__init__.py +49 -0
  593. kiri_migration/scaffold/plans/_registry.py +308 -0
  594. kiri_migration/scaffold/plans/minimal.py +550 -0
  595. kiri_migration/scaffold/plans/plan_b_layer.py +640 -0
  596. kiri_migration/scaffold/silver_from_marts.py +256 -0
  597. kiri_migration/scaffold/silver_pattern.py +451 -0
  598. kiri_migration/sp_translator.py +398 -0
  599. kiri_odcs/LICENSE +204 -0
  600. kiri_odcs/__init__.py +234 -0
  601. kiri_odcs/ai_trust_tier.py +475 -0
  602. kiri_odcs/canonical.py +263 -0
  603. kiri_odcs/diff.py +63 -0
  604. kiri_odcs/errors.py +51 -0
  605. kiri_odcs/invariant_sql.py +136 -0
  606. kiri_odcs/kiri_bridge.py +71 -0
  607. kiri_odcs/kiri_ext.py +3166 -0
  608. kiri_odcs/kiri_fk.py +447 -0
  609. kiri_odcs/kiri_pit.py +121 -0
  610. kiri_odcs/kiri_ref.py +57 -0
  611. kiri_odcs/openlineage_adapter.py +105 -0
  612. kiri_odcs/parser.py +1213 -0
  613. kiri_odcs/pii_guardrails.py +159 -0
  614. kiri_odcs/py.typed +0 -0
  615. kiri_odcs/schema_export.py +223 -0
  616. kiri_openlineage/__init__.py +58 -0
  617. kiri_openlineage/builder.py +146 -0
  618. kiri_openlineage/cost.py +190 -0
  619. kiri_openlineage/emitter.py +165 -0
  620. kiri_openlineage/py.typed +0 -0
  621. kiri_openlineage/schema.py +121 -0
  622. kiri_packs/__init__.py +67 -0
  623. kiri_packs/installer.py +500 -0
  624. kiri_packs/manifest.py +134 -0
  625. kiri_packs/publisher.py +605 -0
  626. kiri_packs/py.typed +0 -0
  627. kiri_packs/validator.py +295 -0
  628. kiri_quality/__init__.py +108 -0
  629. kiri_quality/ai_audit_correlator.py +293 -0
  630. kiri_quality/ai_audit_correlator_apply.py +526 -0
  631. kiri_quality/conformance_evidence.py +272 -0
  632. kiri_quality/dbt_test_collector.py +214 -0
  633. kiri_quality/dq_codegen.py +300 -0
  634. kiri_quality/dv_checks.py +393 -0
  635. kiri_quality/dv_column_lineage.py +279 -0
  636. kiri_quality/dv_column_lineage_apply.py +856 -0
  637. kiri_quality/dv_conformance.py +841 -0
  638. kiri_quality/dv_fixture_runner.py +478 -0
  639. kiri_quality/dv_invariants.py +986 -0
  640. kiri_quality/dv_kiri.py +97 -0
  641. kiri_quality/dv_markdown.py +169 -0
  642. kiri_quality/dv_observed_uc_reader.py +187 -0
  643. kiri_quality/dv_runner.py +389 -0
  644. kiri_quality/evidence_status.py +337 -0
  645. kiri_quality/fk_checks.py +800 -0
  646. kiri_quality/fk_ddl.py +402 -0
  647. kiri_quality/fk_pit_contract.py +168 -0
  648. kiri_quality/fk_runner.py +950 -0
  649. kiri_quality/kernel.py +376 -0
  650. kiri_quality/pii.py +60 -0
  651. kiri_quality/pre_classify/__init__.py +55 -0
  652. kiri_quality/pre_classify/engine.py +346 -0
  653. kiri_quality/pre_classify/locale_packs/en.yaml +117 -0
  654. kiri_quality/pre_classify/locale_packs/sv.yaml +73 -0
  655. kiri_quality/pre_classify/merge.py +297 -0
  656. kiri_quality/pre_classify/model.py +163 -0
  657. kiri_quality/py.typed +0 -0
  658. kiri_quality/reject_lane.py +261 -0
  659. kiri_quality/rule_runner.py +565 -0
  660. kiri_quality/schema_evolution_check.py +88 -0
  661. kiri_quality/silver_quality.py +494 -0
  662. kiri_quality/silver_quality_markdown.py +187 -0
  663. kiri_quality/uc_security_compile.py +951 -0
  664. kiri_quality/uc_security_compile_apply.py +1186 -0
  665. kiri_rbac/__init__.py +87 -0
  666. kiri_rbac/access.py +371 -0
  667. kiri_rbac/loader.py +104 -0
  668. kiri_rbac/pr_gate.py +213 -0
  669. kiri_rbac/providers/__init__.py +29 -0
  670. kiri_rbac/providers/_jwt.py +121 -0
  671. kiri_rbac/providers/azure_ad.py +93 -0
  672. kiri_rbac/providers/base.py +107 -0
  673. kiri_rbac/providers/factory.py +48 -0
  674. kiri_rbac/providers/github.py +134 -0
  675. kiri_rbac/providers/oidc_generic.py +99 -0
  676. kiri_rbac/py.typed +0 -0
  677. kiri_rbac/resolver.py +118 -0
  678. kiri_rbac/schema.py +219 -0
  679. kiri_schema_registry/__init__.py +22 -0
  680. kiri_schema_registry/avro.py +127 -0
  681. kiri_schema_registry/base.py +71 -0
  682. kiri_schema_registry/confluent.py +184 -0
  683. kiri_schema_registry/glue.py +178 -0
  684. kiri_schema_registry/py.typed +0 -0
  685. kiri_slack_bot/__init__.py +24 -0
  686. kiri_slack_bot/handler.py +248 -0
  687. kiri_slack_bot/py.typed +0 -0
  688. kiri_slack_bot/queries.py +90 -0
  689. kiri_slack_bot/signature.py +94 -0
  690. kiri_vault/__init__.py +59 -0
  691. kiri_vault/audit.py +64 -0
  692. kiri_vault/aws_vault.py +123 -0
  693. kiri_vault/azure_vault.py +236 -0
  694. kiri_vault/boundaries.py +219 -0
  695. kiri_vault/client.py +54 -0
  696. kiri_vault/databricks_vault.py +292 -0
  697. kiri_vault/env_vault.py +77 -0
  698. kiri_vault/py.typed +0 -0
  699. kiri_vault/ref.py +84 -0
  700. kirimana_automation/__init__.py +0 -0
  701. kirimana_automation/business_calendar.py +179 -0
  702. kirimana_automation/dag_snapshot.py +252 -0
  703. kirimana_automation/db/__init__.py +5 -0
  704. kirimana_automation/db/migrate.py +571 -0
  705. kirimana_automation/db/migrations/0001_release_state.down.duckdb.sql +29 -0
  706. kirimana_automation/db/migrations/0001_release_state.down.postgres.sql +37 -0
  707. kirimana_automation/db/migrations/0001_release_state.duckdb.sql +170 -0
  708. kirimana_automation/db/migrations/0001_release_state.postgres.sql +176 -0
  709. kirimana_automation/db/migrations/0002_rate_limit_buckets.down.duckdb.sql +9 -0
  710. kirimana_automation/db/migrations/0002_rate_limit_buckets.down.postgres.sql +9 -0
  711. kirimana_automation/db/migrations/0002_rate_limit_buckets.duckdb.sql +14 -0
  712. kirimana_automation/db/migrations/0002_rate_limit_buckets.postgres.sql +18 -0
  713. kirimana_automation/db/migrations/0003_flow_runs.down.duckdb.sql +18 -0
  714. kirimana_automation/db/migrations/0003_flow_runs.down.postgres.sql +19 -0
  715. kirimana_automation/db/migrations/0003_flow_runs.duckdb.sql +85 -0
  716. kirimana_automation/db/migrations/0003_flow_runs.postgres.sql +102 -0
  717. kirimana_automation/db/migrations/0004_flow_dag_snapshots.down.duckdb.sql +11 -0
  718. kirimana_automation/db/migrations/0004_flow_dag_snapshots.down.postgres.sql +11 -0
  719. kirimana_automation/db/migrations/0004_flow_dag_snapshots.duckdb.sql +15 -0
  720. kirimana_automation/db/migrations/0004_flow_dag_snapshots.postgres.sql +31 -0
  721. kirimana_automation/db/migrations/0005_calendar_skip.down.duckdb.sql +109 -0
  722. kirimana_automation/db/migrations/0005_calendar_skip.down.postgres.sql +42 -0
  723. kirimana_automation/db/migrations/0005_calendar_skip.duckdb.sql +113 -0
  724. kirimana_automation/db/migrations/0005_calendar_skip.postgres.sql +42 -0
  725. kirimana_automation/db/migrations/0006_flow_apply_log.down.duckdb.sql +9 -0
  726. kirimana_automation/db/migrations/0006_flow_apply_log.down.postgres.sql +11 -0
  727. kirimana_automation/db/migrations/0006_flow_apply_log.duckdb.sql +45 -0
  728. kirimana_automation/db/migrations/0006_flow_apply_log.postgres.sql +49 -0
  729. kirimana_automation/db/migrations/0007_flow_task_error_redacted.down.duckdb.sql +14 -0
  730. kirimana_automation/db/migrations/0007_flow_task_error_redacted.down.postgres.sql +2 -0
  731. kirimana_automation/db/migrations/0007_flow_task_error_redacted.duckdb.sql +7 -0
  732. kirimana_automation/db/migrations/0007_flow_task_error_redacted.postgres.sql +11 -0
  733. kirimana_automation/db/migrations/README.md +84 -0
  734. kirimana_automation/flow_loader.py +220 -0
  735. kirimana_automation/freeze.py +133 -0
  736. kirimana_automation/kiri_recommender.py +313 -0
  737. kirimana_automation/main.py +195 -0
  738. kirimana_automation/py.typed +0 -0
  739. kirimana_automation/run_history.py +593 -0
  740. kirimana_automation/run_history_analysis.py +607 -0
  741. kirimana_automation/schedule.py +343 -0
  742. kirimana_automation/schedule_validator.py +572 -0
  743. kirimana_automation/workflow_compiler.py +1288 -0
@@ -0,0 +1,488 @@
1
+ """Deterministic source-schema introspection helpers (ADR 0090)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ from collections import Counter
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+ from typing import Any, Protocol
11
+
12
+ import yaml
13
+ from pydantic import BaseModel, ConfigDict
14
+
15
+ from kiri_core.schema.ingestion import (
16
+ DatabaseSpec,
17
+ IngestionFormat,
18
+ IngestionMode,
19
+ RestApiIncremental,
20
+ RestApiPagination,
21
+ RestApiSpec,
22
+ )
23
+ from kiri_core.schema.source import Source
24
+ from kiri_core.types import Classification
25
+
26
+
27
+ class InferredColumn(BaseModel):
28
+ model_config = ConfigDict(extra="forbid", frozen=True)
29
+
30
+ name: str
31
+ data_type: str = "string"
32
+ nullable: bool = True
33
+ description: str | None = None
34
+ pii: bool = False
35
+
36
+
37
+ class RestApiIntrospectionResult(BaseModel):
38
+ model_config = ConfigDict(extra="forbid", frozen=True)
39
+
40
+ record_path: str | None
41
+ columns: tuple[InferredColumn, ...]
42
+ pagination: RestApiPagination
43
+ incremental: RestApiIncremental | None = None
44
+
45
+
46
+ class SourceIntrospectionDraft(BaseModel):
47
+ model_config = ConfigDict(extra="forbid", frozen=True)
48
+
49
+ yaml_text: str
50
+ source_path: Path | None = None
51
+ note: str
52
+
53
+
54
+ class AIColumnAnnotation(BaseModel):
55
+ model_config = ConfigDict(extra="forbid", frozen=True)
56
+
57
+ name: str
58
+ data_type: str | None = None
59
+ description: str | None = None
60
+ pii: bool | None = None
61
+
62
+
63
+ class AISchemaAnnotations(BaseModel):
64
+ model_config = ConfigDict(extra="forbid", frozen=True)
65
+
66
+ columns: tuple[AIColumnAnnotation, ...] = ()
67
+ watermark_column: str | None = None
68
+ watermark_reason: str | None = None
69
+
70
+
71
+ class AIGatewayLike(Protocol):
72
+ def generate(
73
+ self,
74
+ *,
75
+ prompt_name: str,
76
+ variables: dict[str, Any],
77
+ caller: str,
78
+ classification: str,
79
+ ) -> Any: ...
80
+
81
+
82
+ _TIMESTAMP_NAME_RE = re.compile(
83
+ r"(updated|modified|created|time|timestamp|date|datum|systemdatum)",
84
+ re.I,
85
+ )
86
+ _ID_NAME_RE = re.compile(r"(^id$|_id$|id$|nummer$|nr$)", re.I)
87
+ _PII_NAME_RE = re.compile(r"(email|e[-_]?post|phone|telefon|personnummer|ssn|name|namn)", re.I)
88
+
89
+
90
+ def infer_rest_api_schema(payload: Any) -> RestApiIntrospectionResult:
91
+ """Infer record path, columns, pagination, and watermark from one JSON payload."""
92
+ record_path, rows = _best_record_array(payload)
93
+ if not rows:
94
+ raise ValueError("sample JSON contains no array of object records to introspect")
95
+ columns = _infer_columns(rows)
96
+ pagination = _infer_pagination(payload)
97
+ incremental = _infer_incremental(columns)
98
+ return RestApiIntrospectionResult(
99
+ record_path=record_path,
100
+ columns=tuple(columns),
101
+ pagination=pagination,
102
+ incremental=incremental,
103
+ )
104
+
105
+
106
+ def apply_ai_annotations(
107
+ deterministic: RestApiIntrospectionResult,
108
+ annotations: AISchemaAnnotations,
109
+ ) -> RestApiIntrospectionResult:
110
+ """Apply AI annotations only to columns that exist in the deterministic sample."""
111
+ by_name = {col.name: col for col in deterministic.columns}
112
+ updated = dict(by_name)
113
+ for ann in annotations.columns:
114
+ current = updated.get(ann.name)
115
+ if current is None:
116
+ continue
117
+ data_type = ann.data_type if ann.data_type in _TYPE_VOCAB else current.data_type
118
+ updated[ann.name] = current.model_copy(
119
+ update={
120
+ "data_type": data_type,
121
+ "description": ann.description or current.description,
122
+ "pii": current.pii if ann.pii is None else bool(ann.pii),
123
+ }
124
+ )
125
+ incremental = deterministic.incremental
126
+ if annotations.watermark_column in by_name:
127
+ incremental = RestApiIncremental(
128
+ incremental_column=str(annotations.watermark_column),
129
+ request_param="since",
130
+ )
131
+ return deterministic.model_copy(
132
+ update={
133
+ "columns": tuple(updated[name] for name in by_name),
134
+ "incremental": incremental,
135
+ }
136
+ )
137
+
138
+
139
+ def infer_with_ai(
140
+ deterministic: RestApiIntrospectionResult,
141
+ sample_rows: list[dict[str, Any]],
142
+ *,
143
+ gateway: AIGatewayLike,
144
+ source_name: str,
145
+ table_name: str,
146
+ classification: str,
147
+ ) -> RestApiIntrospectionResult:
148
+ """Ask the audited AI gateway for semantic annotations over sampled columns."""
149
+ response = gateway.generate(
150
+ prompt_name="infer_source_schema",
151
+ variables={
152
+ "source_name": source_name,
153
+ "table_name": table_name,
154
+ "columns": json.dumps([c.model_dump() for c in deterministic.columns], sort_keys=True),
155
+ "sample_rows": json.dumps(sample_rows[:20], default=str, sort_keys=True),
156
+ },
157
+ caller="cli.source.introspect.ai",
158
+ classification=classification,
159
+ )
160
+ raw = _strip_fences(str(response.content))
161
+ data = json.loads(raw)
162
+ annotations = AISchemaAnnotations.model_validate(data)
163
+ return apply_ai_annotations(deterministic, annotations)
164
+
165
+
166
+ def render_rest_api_source_draft(
167
+ *,
168
+ project_root: Path,
169
+ source_name: str,
170
+ owner: str,
171
+ classification: Classification,
172
+ domain: str,
173
+ table_name: str,
174
+ rest_api: RestApiSpec,
175
+ inference: RestApiIntrospectionResult,
176
+ ai_used: bool = False,
177
+ ) -> SourceIntrospectionDraft:
178
+ """Render an unreviewed source YAML draft from REST introspection."""
179
+ rest_payload = rest_api.model_copy(
180
+ update={
181
+ "record_path": inference.record_path,
182
+ "pagination": inference.pagination,
183
+ "incremental": inference.incremental,
184
+ }
185
+ ).model_dump(mode="python", exclude_none=True)
186
+ payload = _source_payload(
187
+ source_name=source_name,
188
+ owner=owner,
189
+ classification=classification,
190
+ domain=domain,
191
+ table_name=table_name,
192
+ columns=inference.columns,
193
+ ingestion={
194
+ "mode": IngestionMode.REST_API.value,
195
+ "format": IngestionFormat.JSON.value,
196
+ "rest_api": rest_payload,
197
+ },
198
+ note=(
199
+ "UNREVIEWED source-introspection draft. Review inferred columns/types and "
200
+ "remove kiri.introspection_unreviewed to accept."
201
+ + (" AI annotations were applied through AIGateway." if ai_used else "")
202
+ ),
203
+ )
204
+ Source.model_validate(payload["sources"][0])
205
+ return SourceIntrospectionDraft(
206
+ yaml_text=str(yaml.safe_dump(payload, sort_keys=False, default_flow_style=False)),
207
+ source_path=project_root / "sources" / f"{source_name}.yml",
208
+ note=payload["sources"][0]["kiri"]["introspection_note"],
209
+ )
210
+
211
+
212
+ def render_database_source_draft(
213
+ *,
214
+ project_root: Path,
215
+ source_name: str,
216
+ owner: str,
217
+ classification: Classification,
218
+ domain: str,
219
+ table_name: str,
220
+ database: DatabaseSpec,
221
+ columns: tuple[InferredColumn, ...],
222
+ ) -> SourceIntrospectionDraft:
223
+ payload = _source_payload(
224
+ source_name=source_name,
225
+ owner=owner,
226
+ classification=classification,
227
+ domain=domain,
228
+ table_name=table_name,
229
+ columns=columns,
230
+ ingestion={
231
+ "mode": IngestionMode.DATABASE.value,
232
+ "format": IngestionFormat.JSON.value,
233
+ "database": database.model_dump(mode="python", exclude_none=True),
234
+ },
235
+ note=(
236
+ "UNREVIEWED database-catalog introspection draft. Review inferred "
237
+ "columns/types and remove kiri.introspection_unreviewed to accept."
238
+ ),
239
+ )
240
+ Source.model_validate(payload["sources"][0])
241
+ return SourceIntrospectionDraft(
242
+ yaml_text=str(yaml.safe_dump(payload, sort_keys=False, default_flow_style=False)),
243
+ source_path=project_root / "sources" / f"{source_name}.yml",
244
+ note=payload["sources"][0]["kiri"]["introspection_note"],
245
+ )
246
+
247
+
248
+ def _source_payload(
249
+ *,
250
+ source_name: str,
251
+ owner: str,
252
+ classification: Classification,
253
+ domain: str,
254
+ table_name: str,
255
+ columns: tuple[InferredColumn, ...],
256
+ ingestion: dict[str, Any],
257
+ note: str,
258
+ ) -> dict[str, Any]:
259
+ return {
260
+ "sources": [
261
+ {
262
+ "name": source_name,
263
+ "owner": owner,
264
+ "classification": classification.value,
265
+ "domain": domain,
266
+ "kiri": {
267
+ "introspection_unreviewed": True,
268
+ "introspection_note": note,
269
+ },
270
+ "tables": [
271
+ {
272
+ "name": table_name,
273
+ "columns": [_column_source_payload(col) for col in columns],
274
+ "ingestion": ingestion,
275
+ }
276
+ ],
277
+ }
278
+ ]
279
+ }
280
+
281
+
282
+ def _column_source_payload(col: InferredColumn) -> dict[str, Any]:
283
+ payload: dict[str, Any] = {
284
+ "name": col.name,
285
+ "data_type": col.data_type,
286
+ }
287
+ if col.description:
288
+ payload["description"] = col.description
289
+ if col.pii:
290
+ payload["pii"] = True
291
+ return payload
292
+
293
+
294
+ def _best_record_array(payload: Any) -> tuple[str | None, list[dict[str, Any]]]:
295
+ candidates: list[tuple[str | None, list[dict[str, Any]], int]] = []
296
+
297
+ def walk(value: Any, path: str | None) -> None:
298
+ if isinstance(value, list):
299
+ if value and all(isinstance(item, dict) for item in value):
300
+ rows = [item for item in value if isinstance(item, dict)]
301
+ if _has_homogeneous_keys(rows) and _has_record_scalar(rows):
302
+ common_keys = set(rows[0])
303
+ for row in rows[1:]:
304
+ common_keys &= set(row)
305
+ candidates.append((path, rows, len(rows) * 10 + len(common_keys)))
306
+ return
307
+ if isinstance(value, dict):
308
+ for key, child in value.items():
309
+ child_path = key if path in (None, "") else f"{path}.{key}"
310
+ walk(child, child_path)
311
+
312
+ walk(payload, None)
313
+ if not candidates:
314
+ return None, []
315
+ path, rows, _score = max(
316
+ candidates,
317
+ key=lambda c: (c[2], 0 if c[0] is None else c[0].count(".")),
318
+ )
319
+ return path, rows
320
+
321
+
322
+ def _has_homogeneous_keys(rows: list[dict[str, Any]]) -> bool:
323
+ """Accept record arrays only when every object has the same key set.
324
+
325
+ ADR 0090 drafts must be deterministic and must not union unrelated object
326
+ shapes. Exact key-set equality keeps array detection explainable and fail
327
+ closed for mixed envelopes such as ``[{id, name}, {error, message}]``.
328
+ """
329
+ if not rows:
330
+ return False
331
+ modal = set(rows[0])
332
+ return all(set(row) == modal for row in rows[1:])
333
+
334
+
335
+ def _has_record_scalar(rows: list[dict[str, Any]]) -> bool:
336
+ return any(
337
+ not isinstance(value, (dict, list))
338
+ for row in rows
339
+ for value in row.values()
340
+ if value is not None
341
+ )
342
+
343
+
344
+ def _infer_pagination(payload: Any) -> RestApiPagination:
345
+ """Infer pagination only from explicit payload evidence.
346
+
347
+ Non-paginated payloads default to ``kind='none'`` so unreviewed drafts do
348
+ not fabricate page parameters.
349
+ """
350
+ keys = _payload_keys(payload)
351
+ cursor_markers = {"cursor", "next_cursor", "nextCursor", "next"}
352
+ page_markers = {"page", "total_pages", "totalPages", "offset", "limit", "per_page"}
353
+ if keys & cursor_markers:
354
+ return RestApiPagination(kind="cursor", cursor_param="cursor")
355
+ if keys & page_markers:
356
+ return RestApiPagination(kind="page", page_param="page", limit_param="limit")
357
+ return RestApiPagination(kind="none")
358
+
359
+
360
+ def _payload_keys(payload: Any) -> set[str]:
361
+ keys: set[str] = set()
362
+
363
+ def walk(value: Any) -> None:
364
+ if isinstance(value, dict):
365
+ keys.update(str(key) for key in value)
366
+ for child in value.values():
367
+ walk(child)
368
+ elif isinstance(value, list):
369
+ for item in value[:5]:
370
+ walk(item)
371
+
372
+ walk(payload)
373
+ return keys
374
+
375
+
376
+ _TYPE_VOCAB = {"boolean", "int64", "float64", "timestamp", "date", "string"}
377
+
378
+
379
+ def _infer_columns(rows: list[dict[str, Any]]) -> list[InferredColumn]:
380
+ names = sorted({name for row in rows for name in row})
381
+ columns: list[InferredColumn] = []
382
+ for name in names:
383
+ values = [row.get(name) for row in rows]
384
+ non_null = [v for v in values if v is not None]
385
+ columns.append(
386
+ InferredColumn(
387
+ name=name,
388
+ data_type=_infer_type(name, non_null),
389
+ nullable=len(non_null) != len(values),
390
+ pii=bool(_PII_NAME_RE.search(name)),
391
+ )
392
+ )
393
+ return columns
394
+
395
+
396
+ def _infer_type(name: str, values: list[Any]) -> str:
397
+ if not values:
398
+ return "string"
399
+ if all(isinstance(v, bool) for v in values):
400
+ return "boolean"
401
+ if all(isinstance(v, int) and not isinstance(v, bool) for v in values):
402
+ return "int64"
403
+ if all(isinstance(v, (int, float)) and not isinstance(v, bool) for v in values):
404
+ return "float64"
405
+ if all(isinstance(v, str) and _looks_like_datetime(v) for v in values):
406
+ return "timestamp" if _TIMESTAMP_NAME_RE.search(name) else "date"
407
+ return "string"
408
+
409
+
410
+ def _looks_like_datetime(value: str) -> bool:
411
+ s = value.strip()
412
+ if not s:
413
+ return False
414
+ for candidate in (s, s.replace("Z", "+00:00")):
415
+ try:
416
+ datetime.fromisoformat(candidate)
417
+ return True
418
+ except ValueError:
419
+ continue
420
+ return False
421
+
422
+
423
+ def _infer_incremental(columns: list[InferredColumn]) -> RestApiIncremental | None:
424
+ by_type = [c for c in columns if c.data_type in {"timestamp", "date"}]
425
+ named_time = [c for c in by_type if _TIMESTAMP_NAME_RE.search(c.name)]
426
+ if named_time:
427
+ return RestApiIncremental(incremental_column=named_time[0].name, request_param="since")
428
+ named_id = [c for c in columns if c.data_type == "int64" and _ID_NAME_RE.search(c.name)]
429
+ if named_id:
430
+ return RestApiIncremental(incremental_column=named_id[0].name, request_param="since")
431
+ return None
432
+
433
+
434
+ def columns_from_database_catalog(rows: list[tuple[str, str, bool]]) -> tuple[InferredColumn, ...]:
435
+ """Convert database catalog rows ``(name, db_type, nullable)`` to source columns."""
436
+ return tuple(
437
+ InferredColumn(name=name, data_type=_map_database_type(db_type), nullable=nullable)
438
+ for name, db_type, nullable in rows
439
+ )
440
+
441
+
442
+ def _map_database_type(db_type: str) -> str:
443
+ t = db_type.lower()
444
+ if any(part in t for part in ("int", "serial", "number")):
445
+ return "int64"
446
+ if any(part in t for part in ("double", "float", "real", "decimal", "numeric")):
447
+ return "float64"
448
+ if "bool" in t:
449
+ return "boolean"
450
+ if any(part in t for part in ("timestamp", "datetime")):
451
+ return "timestamp"
452
+ if "date" in t:
453
+ return "date"
454
+ return "string"
455
+
456
+
457
+ def _strip_fences(text: str) -> str:
458
+ s = text.strip()
459
+ if not s.startswith("```"):
460
+ return s
461
+ lines = s.splitlines()
462
+ if lines and lines[0].startswith("```"):
463
+ lines = lines[1:]
464
+ if lines and lines[-1].startswith("```"):
465
+ lines = lines[:-1]
466
+ return "\n".join(lines).strip()
467
+
468
+
469
+ def sample_rows_at_path(payload: Any, record_path: str | None) -> list[dict[str, Any]]:
470
+ current = resolve_dotted_path(payload, record_path)
471
+ if isinstance(current, list):
472
+ return [item for item in current if isinstance(item, dict)]
473
+ return []
474
+
475
+
476
+ def resolve_dotted_path(payload: Any, record_path: str | None) -> Any:
477
+ """Resolve an ADR 0090 dotted JSON object path using literal key names."""
478
+ current = payload
479
+ if record_path:
480
+ for segment in record_path.split("."):
481
+ if not isinstance(current, dict):
482
+ return None
483
+ current = current.get(segment)
484
+ return current
485
+
486
+
487
+ def column_type_counts(columns: tuple[InferredColumn, ...]) -> Counter[str]:
488
+ return Counter(col.data_type for col in columns)