churnkit 1.2.2a2__tar.gz → 1.2.2a4__tar.gz

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 (498) hide show
  1. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/PKG-INFO +1 -1
  2. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/causal_notebooks/c02_archetype_derivation.ipynb +60 -23
  3. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/causal_notebooks/c05_snapshot_and_dashboard.ipynb +22 -96
  4. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/pyproject.toml +1 -1
  5. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/build_causal_notebooks.py +14 -1
  6. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/__init__.py +1 -1
  7. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/dashboard_views.py +75 -4
  8. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/sql/dashboard_views.sql +2 -0
  9. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/.gitignore +0 -0
  10. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/LICENSE +0 -0
  11. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/README.md +0 -0
  12. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/__init__.py +0 -0
  13. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/accounts_view.py +0 -0
  14. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/archetype_view.py +0 -0
  15. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/config.py +0 -0
  16. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/customer_profile.py +0 -0
  17. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/data.py +0 -0
  18. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/default_profile.css +0 -0
  19. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/default_profile.html +0 -0
  20. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/masthead.py +0 -0
  21. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/state.py +0 -0
  22. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/template.py +0 -0
  23. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/theme.css +0 -0
  24. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/apps/databricks_app/src/treemap.py +0 -0
  25. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/causal_notebooks/c01_publish_definitions.ipynb +0 -0
  26. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/causal_notebooks/c03_approval_gate.ipynb +0 -0
  27. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/causal_notebooks/c04_batch_inference.ipynb +0 -0
  28. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/constraints/.gitkeep +0 -0
  29. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/-1_sample_datasets.ipynb +0 -0
  30. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/00_start_here.ipynb +0 -0
  31. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/01_data_discovery.ipynb +0 -0
  32. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/01a_a_temporal_text_deep_dive.ipynb +0 -0
  33. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/01a_temporal_deep_dive.ipynb +0 -0
  34. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/01b_temporal_quality.ipynb +0 -0
  35. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/01c_temporal_patterns.ipynb +0 -0
  36. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/01d_event_aggregation.ipynb +0 -0
  37. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/02_source_integrity.ipynb +0 -0
  38. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/03_dataset_merge.ipynb +0 -0
  39. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/04_column_deep_dive.ipynb +0 -0
  40. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/04a_text_columns_deep_dive.ipynb +0 -0
  41. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/05_relationship_analysis.ipynb +0 -0
  42. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/06_feature_opportunities.ipynb +0 -0
  43. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/07_modeling_readiness.ipynb +0 -0
  44. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/08_baseline_experiments.ipynb +0 -0
  45. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/09_business_alignment.ipynb +0 -0
  46. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/10_spec_generation.ipynb +0 -0
  47. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/11_scoring_validation.ipynb +0 -0
  48. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/exploration_notebooks/12_view_documentation.ipynb +0 -0
  49. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/.claude/settings.local.json +0 -0
  50. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/README.md +0 -0
  51. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/build_framework_phase_map.py +0 -0
  52. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/data/create_snapshot.py +0 -0
  53. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/data/generate_edi_ticketing_dataset.py +0 -0
  54. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/data/generate_retail_dataset.py +0 -0
  55. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/data/generate_test_data.py +0 -0
  56. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/data/migrate_parquet_to_delta.py +0 -0
  57. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/data/migrate_to_temporal.py +0 -0
  58. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/databricks/build_wheel.sh +0 -0
  59. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/databricks/capture_runtime.py +0 -0
  60. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/databricks/dbr_init.sh +0 -0
  61. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/databricks/deploy_dev.py +0 -0
  62. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/databricks/generate_constraints.py +0 -0
  63. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/databricks/notebook_setup.py +0 -0
  64. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/experiments/capture_notebook_outputs.py +0 -0
  65. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/experiments/cell_profiling.py +0 -0
  66. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/experiments/compare_exploration_runs.py +0 -0
  67. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/experiments/compare_versions.sh +0 -0
  68. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/experiments/patch_dataset_config.py +0 -0
  69. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/generate_requirements.py +0 -0
  70. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/clean_notebook_outputs.py +0 -0
  71. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/export_tutorial_html.py +0 -0
  72. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/init_project.py +0 -0
  73. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/migrate_notebook_cell_ids.py +0 -0
  74. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/plotly_image_preprocessor.py +0 -0
  75. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/run_exploration.py +0 -0
  76. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/sync_notebooks.py +0 -0
  77. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/tag_framework_cells.py +0 -0
  78. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/tag_markdown_cells.py +0 -0
  79. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/notebooks/test_notebooks.py +0 -0
  80. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/release.sh +0 -0
  81. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/seed_playbooks_volume.py +0 -0
  82. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/templates/tutorial_html/conf.json +0 -0
  83. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/templates/tutorial_html/index.html.j2 +0 -0
  84. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/scripts/update_notebook_paths.py +0 -0
  85. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/__init__.py +0 -0
  86. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/__init__.py +0 -0
  87. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/active_dataset_store.py +0 -0
  88. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/analysis_context.py +0 -0
  89. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/column_describer.py +0 -0
  90. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/dataset_fingerprinter.py +0 -0
  91. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/entity_timestamp_deriver.py +0 -0
  92. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/exploration_manager.py +0 -0
  93. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/explorer.py +0 -0
  94. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/field_availability_audit.py +0 -0
  95. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/findings.py +0 -0
  96. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/intent_defaults.py +0 -0
  97. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/key_resolver.py +0 -0
  98. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/layered_recommendations.py +0 -0
  99. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/objective_support_communicator.py +0 -0
  100. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/prediction_objective_detector.py +0 -0
  101. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/project_context.py +0 -0
  102. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/recommendation_builder.py +0 -0
  103. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/recommendations.py +0 -0
  104. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/run_namespace.py +0 -0
  105. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/sampling.py +0 -0
  106. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/schema_report.py +0 -0
  107. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/service_unit_detector.py +0 -0
  108. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/session.py +0 -0
  109. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/skip_logic.py +0 -0
  110. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/auto_explorer/snapshot_grid.py +0 -0
  111. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/__init__.py +0 -0
  112. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/ab_test_designer.py +0 -0
  113. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/fairness_analyzer.py +0 -0
  114. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/intervention_matcher.py +0 -0
  115. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/ratio_features.py +0 -0
  116. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/report_generator.py +0 -0
  117. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/risk_profile.py +0 -0
  118. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/business/roi_analyzer.py +0 -0
  119. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/__init__.py +0 -0
  120. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/calibration_analyzer.py +0 -0
  121. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/cv_analyzer.py +0 -0
  122. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/error_analyzer.py +0 -0
  123. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/exploration_ledger.py +0 -0
  124. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/feature_provenance.py +0 -0
  125. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/feature_stability.py +0 -0
  126. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/leakage_detector.py +0 -0
  127. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/model_diagnostics_report.py +0 -0
  128. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/noise_tester.py +0 -0
  129. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/overfitting_analyzer.py +0 -0
  130. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/parity_report.py +0 -0
  131. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/diagnostics/segment_analyzer.py +0 -0
  132. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/discovery/__init__.py +0 -0
  133. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/discovery/config_generator.py +0 -0
  134. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/discovery/discovery_flow.py +0 -0
  135. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/discovery/type_inferencer.py +0 -0
  136. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/interpretability/__init__.py +0 -0
  137. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/interpretability/cohort_analyzer.py +0 -0
  138. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/interpretability/counterfactual.py +0 -0
  139. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/interpretability/individual_explainer.py +0 -0
  140. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/interpretability/pdp_generator.py +0 -0
  141. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/interpretability/shap_explainer.py +0 -0
  142. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/jupyter_save_hook.py +0 -0
  143. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/notebook_html_exporter.py +0 -0
  144. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/notebook_progress.py +0 -0
  145. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/plotly_preprocessor.py +0 -0
  146. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/__init__.py +0 -0
  147. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/base.py +0 -0
  148. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/cleaning/__init__.py +0 -0
  149. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/cleaning/consistency.py +0 -0
  150. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/cleaning/deduplicate.py +0 -0
  151. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/cleaning/impute.py +0 -0
  152. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/cleaning/outlier.py +0 -0
  153. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/datetime/__init__.py +0 -0
  154. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/datetime/extract.py +0 -0
  155. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/encoding/__init__.py +0 -0
  156. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/encoding/categorical.py +0 -0
  157. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/pipeline.py +0 -0
  158. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/registry.py +0 -0
  159. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/selection/__init__.py +0 -0
  160. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/selection/drop_column.py +0 -0
  161. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/transform/__init__.py +0 -0
  162. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/transform/power.py +0 -0
  163. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/recommendations/transform/scale.py +0 -0
  164. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/visualization/__init__.py +0 -0
  165. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/visualization/attention_scorer.py +0 -0
  166. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/visualization/chart_builder.py +0 -0
  167. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/visualization/column_paginator.py +0 -0
  168. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/visualization/console.py +0 -0
  169. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/visualization/display.py +0 -0
  170. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/analysis/visualization/number_formatter.py +0 -0
  171. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/artifacts/__init__.py +0 -0
  172. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/artifacts/fit_artifact_registry.py +0 -0
  173. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/cli.py +0 -0
  174. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/__init__.py +0 -0
  175. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/__init__.py +0 -0
  176. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/bulk_profiling.py +0 -0
  177. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/cell_profiling_hooks.py +0 -0
  178. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/detection.py +0 -0
  179. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/ops.py +0 -0
  180. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/pandas_backend.py +0 -0
  181. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/remote_path.py +0 -0
  182. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/spark_backend.py +0 -0
  183. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/compat/timing.py +0 -0
  184. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/__init__.py +0 -0
  185. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/base.py +0 -0
  186. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/__init__.py +0 -0
  187. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/deployer.py +0 -0
  188. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/explainer.py +0 -0
  189. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/feature_eng.py +0 -0
  190. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/ingester.py +0 -0
  191. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/profiler.py +0 -0
  192. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/trainer.py +0 -0
  193. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/transformer.py +0 -0
  194. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/components/validator.py +0 -0
  195. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/enums.py +0 -0
  196. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/orchestrator.py +0 -0
  197. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/components/registry.py +0 -0
  198. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/config/__init__.py +0 -0
  199. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/config/column_config.py +0 -0
  200. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/config/experiments.py +0 -0
  201. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/config/pipeline_config.py +0 -0
  202. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/config/source_config.py +0 -0
  203. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/naming.py +0 -0
  204. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/utils/__init__.py +0 -0
  205. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/utils/leakage.py +0 -0
  206. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/utils/severity.py +0 -0
  207. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/core/utils/statistics.py +0 -0
  208. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/__init__.py +0 -0
  209. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/__init__.py +0 -0
  210. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/base.py +0 -0
  211. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/cell_builder.py +0 -0
  212. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/config.py +0 -0
  213. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/databricks_generator.py +0 -0
  214. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/local_generator.py +0 -0
  215. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/project_init.py +0 -0
  216. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/runner.py +0 -0
  217. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/scoring_replay.py +0 -0
  218. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/script_generator.py +0 -0
  219. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/__init__.py +0 -0
  220. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/base_stage.py +0 -0
  221. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/causal_setup_cell.py +0 -0
  222. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s01_ingestion.py +0 -0
  223. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s02_profiling.py +0 -0
  224. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s03_cleaning.py +0 -0
  225. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s04_transformation.py +0 -0
  226. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s05_feature_engineering.py +0 -0
  227. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s06_feature_selection.py +0 -0
  228. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s07_model_training.py +0 -0
  229. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s08_deployment.py +0 -0
  230. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s09_monitoring.py +0 -0
  231. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s10_batch_inference.py +0 -0
  232. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s11_feature_store.py +0 -0
  233. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s_c01_publish_definitions.py +0 -0
  234. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s_c02_archetype_derivation.py +0 -0
  235. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s_c03_approval_gate.py +0 -0
  236. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s_c04_batch_inference.py +0 -0
  237. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_generator/stages/s_c05_snapshot_and_dashboard.py +0 -0
  238. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_merge/__init__.py +0 -0
  239. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_merge/cli.py +0 -0
  240. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_merge/config_merger.py +0 -0
  241. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_merge/config_parser.py +0 -0
  242. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_merge/conflict.py +0 -0
  243. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_merge/merge_engine.py +0 -0
  244. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_merge/merge_report.py +0 -0
  245. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_sync/__init__.py +0 -0
  246. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_sync/cell_id_standardizer.py +0 -0
  247. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_sync/cell_types.py +0 -0
  248. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_sync/cli.py +0 -0
  249. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_sync/sync_engine.py +0 -0
  250. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/notebook_sync/sync_report.py +0 -0
  251. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/orchestration/__init__.py +0 -0
  252. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/orchestration/code_generator.py +0 -0
  253. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/orchestration/context.py +0 -0
  254. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/orchestration/data_materializer.py +0 -0
  255. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/orchestration/databricks_exporter.py +0 -0
  256. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/orchestration/doc_generator.py +0 -0
  257. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/__init__.py +0 -0
  258. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/databricks_generator.py +0 -0
  259. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/databricks_renderer.py +0 -0
  260. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/exploration_generator.py +0 -0
  261. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/findings_parser.py +0 -0
  262. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/generation_manifest.py +0 -0
  263. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/generator.py +0 -0
  264. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/gold_transform_applicator.py +0 -0
  265. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/llm_docs_generator.py +0 -0
  266. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/models.py +0 -0
  267. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/override_merge.py +0 -0
  268. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/protocols.py +0 -0
  269. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/renderer.py +0 -0
  270. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/pipeline_generator/user_extensions_emitter.py +0 -0
  271. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/spec_generator/__init__.py +0 -0
  272. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/generators/spec_generator/mlflow_pipeline_generator.py +0 -0
  273. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/__init__.py +0 -0
  274. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/__init__.py +0 -0
  275. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/base.py +0 -0
  276. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/factory.py +0 -0
  277. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/feature_store/__init__.py +0 -0
  278. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/feature_store/base.py +0 -0
  279. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/feature_store/databricks.py +0 -0
  280. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/feature_store/feast_adapter.py +0 -0
  281. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/feature_store/local.py +0 -0
  282. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/mlflow/__init__.py +0 -0
  283. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/mlflow/base.py +0 -0
  284. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/mlflow/databricks.py +0 -0
  285. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/mlflow/experiment_tracker.py +0 -0
  286. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/mlflow/local.py +0 -0
  287. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/storage/__init__.py +0 -0
  288. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/storage/base.py +0 -0
  289. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/storage/databricks.py +0 -0
  290. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/adapters/storage/local.py +0 -0
  291. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/databricks_init.py +0 -0
  292. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/databricks_job_capture.py +0 -0
  293. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/feature_store/__init__.py +0 -0
  294. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/feature_store/definitions.py +0 -0
  295. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/feature_store/manager.py +0 -0
  296. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/feature_store/registry.py +0 -0
  297. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/iteration/__init__.py +0 -0
  298. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/iteration/context.py +0 -0
  299. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/iteration/feedback_collector.py +0 -0
  300. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/iteration/orchestrator.py +0 -0
  301. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/iteration/recommendation_tracker.py +0 -0
  302. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/iteration/signals.py +0 -0
  303. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/llm_context/__init__.py +0 -0
  304. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/llm_context/context_builder.py +0 -0
  305. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/llm_context/prompts.py +0 -0
  306. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/requirements_generator.py +0 -0
  307. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/__init__.py +0 -0
  308. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/batch_integration.py +0 -0
  309. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/early_warning_model.py +0 -0
  310. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/event_schema.py +0 -0
  311. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/online_store_writer.py +0 -0
  312. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/realtime_scorer.py +0 -0
  313. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/trigger_engine.py +0 -0
  314. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/integrations/streaming/window_aggregator.py +0 -0
  315. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/__init__.py +0 -0
  316. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/api.py +0 -0
  317. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/decorator.py +0 -0
  318. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/flags.py +0 -0
  319. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/harvest.py +0 -0
  320. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/logging.py +0 -0
  321. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/registry.py +0 -0
  322. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/replay.py +0 -0
  323. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/runtime/validation.py +0 -0
  324. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/__init__.py +0 -0
  325. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/__init__.py +0 -0
  326. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/approval_gate.py +0 -0
  327. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/clusterer.py +0 -0
  328. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/column_descriptions_writer.py +0 -0
  329. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/dashboard_profile_override.py +0 -0
  330. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/delta_writer.py +0 -0
  331. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/derivation.py +0 -0
  332. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/feature_meta_writer.py +0 -0
  333. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/__init__.py +0 -0
  334. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/archetype_context.py +0 -0
  335. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/business_phrase.py +0 -0
  336. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/discovery.py +0 -0
  337. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/enrichment_pipeline.py +0 -0
  338. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/feature_meta_builder.py +0 -0
  339. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/llm_prompt.py +0 -0
  340. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/markdown_bootstrap.py +0 -0
  341. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/predicate_prose.py +0 -0
  342. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/prose_backfill.py +0 -0
  343. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/quantile_phrasing.py +0 -0
  344. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/interpretation/sidecars.py +0 -0
  345. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/llm_namer.py +0 -0
  346. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/playbook_loader.py +0 -0
  347. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/playbook_mapper.py +0 -0
  348. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/policy_loader.py +0 -0
  349. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/population_stats.py +0 -0
  350. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/predicate_compiler.py +0 -0
  351. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/rule_extractor.py +0 -0
  352. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/run_context_writer.py +0 -0
  353. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/schemas.py +0 -0
  354. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/seed_yamls/decision_policy.yaml +0 -0
  355. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/seed_yamls/response_schemas.yaml +0 -0
  356. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/seed_yamls/vocabularies.yaml +0 -0
  357. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/shap_runner.py +0 -0
  358. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/snapshot_writer.py +0 -0
  359. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/causal/top_drivers_writer.py +0 -0
  360. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/cleaning/__init__.py +0 -0
  361. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/cleaning/base.py +0 -0
  362. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/cleaning/missing_handler.py +0 -0
  363. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/cleaning/outlier_handler.py +0 -0
  364. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/deployment/__init__.py +0 -0
  365. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/deployment/batch_scorer.py +0 -0
  366. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/deployment/champion_challenger.py +0 -0
  367. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/deployment/model_registry.py +0 -0
  368. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/deployment/retraining_trigger.py +0 -0
  369. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/__init__.py +0 -0
  370. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/behavioral_features.py +0 -0
  371. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/customer_segmentation.py +0 -0
  372. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/feature_definitions.py +0 -0
  373. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/feature_engineer.py +0 -0
  374. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/feature_manifest.py +0 -0
  375. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/feature_selector.py +0 -0
  376. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/interaction_features.py +0 -0
  377. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/features/temporal_features.py +0 -0
  378. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/ingestion/__init__.py +0 -0
  379. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/ingestion/load_result.py +0 -0
  380. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/ingestion/loaders.py +0 -0
  381. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/ingestion/source_registry.py +0 -0
  382. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/lifecycle/__init__.py +0 -0
  383. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/lifecycle/config.py +0 -0
  384. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/lifecycle/enrich.py +0 -0
  385. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/lifecycle/validation.py +0 -0
  386. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/__init__.py +0 -0
  387. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/baseline_trainer.py +0 -0
  388. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/cross_validator.py +0 -0
  389. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/data_splitter.py +0 -0
  390. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/feature_profile.py +0 -0
  391. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/feature_scaler.py +0 -0
  392. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/feature_spec.py +0 -0
  393. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/hyperparameter_tuner.py +0 -0
  394. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/imbalance_handler.py +0 -0
  395. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/mlflow_logger.py +0 -0
  396. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/model_comparator.py +0 -0
  397. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/model_evaluator.py +0 -0
  398. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/shap_attribution.py +0 -0
  399. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/spark_baseline_trainer.py +0 -0
  400. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/spark_classifier_wrapper.py +0 -0
  401. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/spark_feature_scaler.py +0 -0
  402. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/threshold_optimizer.py +0 -0
  403. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/modeling/training_preparator.py +0 -0
  404. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/monitoring/__init__.py +0 -0
  405. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/monitoring/alert_manager.py +0 -0
  406. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/monitoring/drift_detector.py +0 -0
  407. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/monitoring/performance_monitor.py +0 -0
  408. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/preprocessing/__init__.py +0 -0
  409. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/preprocessing/transformer_manager.py +0 -0
  410. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/__init__.py +0 -0
  411. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/categorical_distribution.py +0 -0
  412. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/categorical_target_analyzer.py +0 -0
  413. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/column_profiler.py +0 -0
  414. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/distribution_analysis.py +0 -0
  415. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/drift_detector.py +0 -0
  416. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/feature_capacity.py +0 -0
  417. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/pattern_analysis_config.py +0 -0
  418. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/profile_result.py +0 -0
  419. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/quality_checks.py +0 -0
  420. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/recommendation_filter.py +0 -0
  421. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/relationship_detector.py +0 -0
  422. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/relationship_recommender.py +0 -0
  423. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/report_generator.py +0 -0
  424. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/scd_analyzer.py +0 -0
  425. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/segment_analyzer.py +0 -0
  426. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/segment_aware_outlier.py +0 -0
  427. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/spark_segment_analyzer.py +0 -0
  428. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/spark_temporal_feature_analyzer.py +0 -0
  429. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/spark_temporal_feature_engineer.py +0 -0
  430. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/spark_time_window_aggregator.py +0 -0
  431. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/stats_helpers.py +0 -0
  432. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/target_level_analyzer.py +0 -0
  433. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/temporal_analyzer.py +0 -0
  434. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/temporal_coverage.py +0 -0
  435. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/temporal_feature_analyzer.py +0 -0
  436. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/temporal_feature_engineer.py +0 -0
  437. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/temporal_pattern_analyzer.py +0 -0
  438. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/temporal_quality_checks.py +0 -0
  439. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/temporal_target_analyzer.py +0 -0
  440. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/text_embedder.py +0 -0
  441. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/text_processor.py +0 -0
  442. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/text_reducer.py +0 -0
  443. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/time_series_profiler.py +0 -0
  444. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/time_window_aggregator.py +0 -0
  445. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/type_detector.py +0 -0
  446. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/profiling/window_recommendation.py +0 -0
  447. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scd_history/__init__.py +0 -0
  448. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scd_history/augment.py +0 -0
  449. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scd_history/config.py +0 -0
  450. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scd_history/reconstruct.py +0 -0
  451. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scd_history/validation.py +0 -0
  452. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scoring/__init__.py +0 -0
  453. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scoring/batch_inference.py +0 -0
  454. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scoring/config.py +0 -0
  455. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scoring/data_loader.py +0 -0
  456. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scoring/exceptions.py +0 -0
  457. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/scoring/pipeline_discovery.py +0 -0
  458. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/__init__.py +0 -0
  459. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/access_guard.py +0 -0
  460. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/data_preparer.py +0 -0
  461. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/point_in_time_join.py +0 -0
  462. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/point_in_time_registry.py +0 -0
  463. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/scenario_detector.py +0 -0
  464. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/snapshot_manager.py +0 -0
  465. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/spark_temporal_merger.py +0 -0
  466. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/synthetic_coordinator.py +0 -0
  467. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/temporal_merger.py +0 -0
  468. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/timestamp_discovery.py +0 -0
  469. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/temporal/timestamp_manager.py +0 -0
  470. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/transformation/__init__.py +0 -0
  471. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/transformation/binary_handler.py +0 -0
  472. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/transformation/categorical_encoder.py +0 -0
  473. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/transformation/datetime_transformer.py +0 -0
  474. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/transformation/numeric_transformer.py +0 -0
  475. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/transformation/pipeline.py +0 -0
  476. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/__init__.py +0 -0
  477. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/adversarial_scoring_validator.py +0 -0
  478. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/business_sense_gate.py +0 -0
  479. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/data_quality_gate.py +0 -0
  480. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/data_validators.py +0 -0
  481. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/feature_quality_gate.py +0 -0
  482. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/gates.py +0 -0
  483. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/leakage_gate.py +0 -0
  484. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/model_validity_gate.py +0 -0
  485. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/pipeline_validation_runner.py +0 -0
  486. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/quality_scorer.py +0 -0
  487. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/rule_generator.py +0 -0
  488. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/scoring_pipeline_validator.py +0 -0
  489. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/stages/validation/timeseries_detector.py +0 -0
  490. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/transforms/__init__.py +0 -0
  491. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/transforms/artifact_store.py +0 -0
  492. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/transforms/executor.py +0 -0
  493. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/transforms/fitted.py +0 -0
  494. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/transforms/ops.py +0 -0
  495. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/src/customer_retention/transforms/spark_ops.py +0 -0
  496. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/tests/exploration_notebooks/test_distributed_dtype_safety.py +0 -0
  497. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/tests/scripts/__init__.py +0 -0
  498. {churnkit-1.2.2a2 → churnkit-1.2.2a4}/tests/scripts/test_build_framework_phase_map.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: churnkit
3
- Version: 1.2.2a2
3
+ Version: 1.2.2a4
4
4
  Summary: Structured ML framework for customer churn prediction -- from exploration notebooks to production pipelines, locally or on Databricks.
5
5
  Project-URL: Homepage, https://github.com/aladjov/CR
6
6
  Project-URL: Documentation, https://github.com/aladjov/CR/wiki
@@ -4,7 +4,20 @@
4
4
  "cell_type": "markdown",
5
5
  "id": "046ea43d",
6
6
  "metadata": {},
7
- "source": "[//]: # (cr:doc name='chapter_c02_archetype_derivation' id=046ea43d)\n# Chapter c02: Archetype Derivation (Causal Track)\n\nHeart of the causal track:\n1. Loads the `@production` model + the gold features.\n2. Loads the frozen `ShapAttribution` artifact persisted with the training run (per-feature importance + background means — no background sample is re-computed here).\n3. Emits per-row linear SHAP via a single distributed `spark_df.select(...)` — `shap_i(row) = importance_i * (x_i - background_mean_i)`. No `TreeExplainer`, no `pandas_udf`.\n4. Pre-selects top features by mean |SHAP| (`KMEANS_FEATURE_CAP`).\n5. Runs a silhouette-swept Spark KMeans over the reduced SHAP space.\n6. Fits per-cluster surrogate trees → JSON predicates.\n7. Maps archetypes ↔ playbooks via feature-overlap baseline + optional LLM refinement.\n8. Writes `archetype_catalog` + `eligibility_policy` rows as `pending_review` for the c03 approval gate.\n"
7
+ "source": [
8
+ "[//]: # (cr:doc name='chapter_c02_archetype_derivation' id=046ea43d)\n",
9
+ "# Chapter c02: Archetype Derivation (Causal Track)\n",
10
+ "\n",
11
+ "Heart of the causal track:\n",
12
+ "1. Loads the `@production` model + the gold features.\n",
13
+ "2. Freezes a stratified SHAP background sample.\n",
14
+ "3. Computes per-row SHAP via partition-wise `pandas_udf` (TreeExplainer with the frozen background).\n",
15
+ "4. Pre-selects top features by mean |SHAP| (`KMEANS_FEATURE_CAP`).\n",
16
+ "5. Runs a silhouette-swept Spark KMeans over the reduced SHAP space.\n",
17
+ "6. Fits per-cluster surrogate trees → JSON predicates.\n",
18
+ "7. Maps archetypes ↔ playbooks via feature-overlap baseline + optional LLM refinement.\n",
19
+ "8. Writes `archetype_catalog` + `eligibility_policy` rows as `pending_review` for the c03 approval gate.\n"
20
+ ]
8
21
  },
9
22
  {
10
23
  "cell_type": "code",
@@ -236,22 +249,6 @@
236
249
  " llm_namer = build_llm_namer(LLM_ENDPOINT_NAME)\n",
237
250
  " print(f\"Resolved LLM matcher: {llm_namer.model_id}\")\n",
238
251
  "\n",
239
- " try:\n",
240
- " from customer_retention.analysis.auto_explorer.run_namespace import RunNamespace\n",
241
- " from customer_retention.stages.causal.interpretation import (\n",
242
- " enrich_archetype_from_namespace,\n",
243
- " )\n",
244
- " _enrich_ns = RunNamespace.from_env_or_latest(get_experiments_dir())\n",
245
- "\n",
246
- " def _enrichment_builder(_arch, _ctx):\n",
247
- " return enrich_archetype_from_namespace(\n",
248
- " _ctx, _enrich_ns, composite_name=COMPOSITE_NAME or \"\",\n",
249
- " )\n",
250
- " print(f\"Enrichment builder active: namespace={_enrich_ns.run_dir}\")\n",
251
- " except Exception as _enrich_exc:\n",
252
- " print(f\"Enrichment disabled ({type(_enrich_exc).__name__}: {_enrich_exc}) — LLM prompt falls back to raw column names.\")\n",
253
- " _enrichment_builder = None\n",
254
- "\n",
255
252
  " cfg = DerivationConfig(\n",
256
253
  " spark=spark,\n",
257
254
  " training_df=training_df,\n",
@@ -271,8 +268,6 @@
271
268
  " feature_cap=KMEANS_FEATURE_CAP,\n",
272
269
  " llm_endpoint_name=LLM_ENDPOINT_NAME,\n",
273
270
  " llm_namer=llm_namer,\n",
274
- " enrichment_builder=_enrichment_builder,\n",
275
- " namespace=_enrich_ns,\n",
276
271
  " fit_thresholds=FitThresholds(\n",
277
272
  " auto=float(FIT_AUTO_THRESHOLD),\n",
278
273
  " review=float(FIT_REVIEW_THRESHOLD),\n",
@@ -288,12 +283,54 @@
288
283
  ]
289
284
  },
290
285
  {
291
- "cell_type": "code",
292
- "id": "a20abbe5",
293
- "source": "# @cr:code name='backfill_eligibility_rules_prose' id=a20abbe5\n# Re-render `eligibility_rules_prose` for every existing active policy row whose\n# prose column is NULL — derivation populates the column at row-creation time\n# only, so rows written before column_descriptions / feature_meta /\n# feature_population_stats sidecars existed stay NULL until we rewrite them.\n# Cycle 013 P4 surfaced exactly this gap; the backfill closes it idempotently.\nif is_databricks() and spark is not None and spark.catalog.tableExists(ELIGIBILITY_POLICY_FQN):\n from customer_retention.stages.causal.interpretation import (\n backfill_eligibility_prose,\n )\n\n _backfill = backfill_eligibility_prose(\n spark, ELIGIBILITY_POLICY_FQN, namespace=_enrich_ns,\n )\n print(_backfill.summary())\n if _backfill.warnings:\n print(\"(interpretation-layer warnings — see logs for details)\")\n for _w in _backfill.warnings:\n print(f\" - {_w}\")\n",
286
+ "cell_type": "markdown",
287
+ "id": "2e42d59c",
294
288
  "metadata": {},
289
+ "source": [
290
+ "[//]: # (cr:doc name='c02_backfill_prose_section' id=2e42d59c)\n",
291
+ "## 2.2 Backfill `eligibility_rules_prose`\n"
292
+ ]
293
+ },
294
+ {
295
+ "cell_type": "code",
295
296
  "execution_count": null,
296
- "outputs": []
297
+ "id": "04993f07",
298
+ "metadata": {},
299
+ "outputs": [],
300
+ "source": [
301
+ "# @cr:code name='backfill_eligibility_rules_prose' id=04993f07\n",
302
+ "# Re-render `eligibility_rules_prose` for every existing active policy row whose\n",
303
+ "# prose column is NULL — derivation populates the column at row-creation time\n",
304
+ "# only, so rows written before column_descriptions / feature_meta /\n",
305
+ "# feature_population_stats sidecars existed stay NULL until we rewrite them.\n",
306
+ "# Cycle 013 P4 surfaced exactly this gap; the backfill closes it idempotently.\n",
307
+ "if is_databricks() and spark is not None and spark.catalog.tableExists(ELIGIBILITY_POLICY_FQN):\n",
308
+ " from customer_retention.analysis.auto_explorer.run_namespace import RunNamespace\n",
309
+ " from customer_retention.stages.causal.interpretation import (\n",
310
+ " backfill_eligibility_prose,\n",
311
+ " )\n",
312
+ "\n",
313
+ " # Resolve the namespace locally so this cell runs standalone — the\n",
314
+ " # derivation cell only defines `_enrich_ns` on the freshly-derive path,\n",
315
+ " # so a backfill-only rerun (active archetypes already exist) would\n",
316
+ " # otherwise NameError on the reference below.\n",
317
+ " _backfill_ns = globals().get(\"_enrich_ns\")\n",
318
+ " if _backfill_ns is None:\n",
319
+ " try:\n",
320
+ " _backfill_ns = RunNamespace.from_env_or_latest(get_experiments_dir())\n",
321
+ " except Exception as _ns_exc:\n",
322
+ " print(f\"Backfill namespace lookup failed ({type(_ns_exc).__name__}: {_ns_exc}) — proceeding without sidecar context.\")\n",
323
+ " _backfill_ns = None\n",
324
+ "\n",
325
+ " _backfill = backfill_eligibility_prose(\n",
326
+ " spark, ELIGIBILITY_POLICY_FQN, namespace=_backfill_ns,\n",
327
+ " )\n",
328
+ " print(_backfill.summary())\n",
329
+ " if _backfill.warnings:\n",
330
+ " print(\"(interpretation-layer warnings — see logs for details)\")\n",
331
+ " for _w in _backfill.warnings:\n",
332
+ " print(f\" - {_w}\")\n"
333
+ ]
297
334
  },
298
335
  {
299
336
  "cell_type": "code",
@@ -63,7 +63,10 @@
63
63
  "cell_type": "markdown",
64
64
  "id": "96b40ce2",
65
65
  "metadata": {},
66
- "source": "[//]: # (cr:doc name='c05_configuration' id=96b40ce2)\n## Configuration\n\nThe cell below is the only place you should need to edit. Every value here is read by the snapshot writer, the per-slice SHAP writer, and the dashboard publisher — nothing is hardcoded inside the algorithmic cells.\n\n- **`SNAPSHOT_RISK_TIER_HIGH` / `SNAPSHOT_RISK_TIER_MEDIUM`** — risk-tier thresholds applied at snapshot time. Leave as `None` to fall back to the values stored on the active `decision_policy` row (the canonical source — set in `c01_publish_definitions`).\n- **`SNAPSHOT_CAPACITY_PARTITION_COLUMN`** — optional partition column for capacity caps (e.g. `\"csm_owner_id\"`). Leave as `\"\"` to apply caps globally per playbook.\n- **`SHAP_PER_SLICE_K`** — per `(playbook, archetype, risk_tier)` slice, this many top-ranked accounts (by `expected_loss = churn_probability × value_at_risk`, breaking ties on `churn_probability`) get per-row SHAP attributed and persisted to `top_shap_drivers`. This is the **only** cap controlling how many rows reach the L3 cohort dashboard view (`v_eligible_all_playbooks`) — slices without SHAP simply do not surface. Set to `0` to skip SHAP enrichment entirely.\n- **`SHAP_TOP_DRIVERS_PER_ROW`** — number of top drivers (by `|shap_contribution|`) emitted per scored account into the `top_drivers` array."
66
+ "source": [
67
+ "[//]: # (cr:doc name='c05_configuration' id=96b40ce2)\n",
68
+ "## Configuration\n\nThe cell below is the only place you should need to edit. Every value here is read by the snapshot writer and the dashboard publisher — nothing is hardcoded inside the algorithmic cells.\n\n- **`SNAPSHOT_RISK_TIER_HIGH` / `SNAPSHOT_RISK_TIER_MEDIUM`** — risk-tier thresholds applied at snapshot time. Leave as `None` to fall back to the values stored on the active `decision_policy` row (the canonical source — set in `c01_publish_definitions`).\n- **`SNAPSHOT_CAPACITY_PARTITION_COLUMN`** — optional partition column for capacity caps (e.g. `\"csm_owner_id\"`). Leave as `\"\"` to apply caps globally per playbook.\n"
69
+ ]
67
70
  },
68
71
  {
69
72
  "cell_type": "code",
@@ -75,10 +78,7 @@
75
78
  "# @cr:config name='configuration' id=2586e099\n",
76
79
  "SNAPSHOT_RISK_TIER_HIGH = None\n",
77
80
  "SNAPSHOT_RISK_TIER_MEDIUM = None\n",
78
- "SNAPSHOT_CAPACITY_PARTITION_COLUMN = \"\"\n",
79
- "\n",
80
- "SHAP_PER_SLICE_K = 20\n",
81
- "SHAP_TOP_DRIVERS_PER_ROW = 5\n"
81
+ "SNAPSHOT_CAPACITY_PARTITION_COLUMN = \"\"\n"
82
82
  ]
83
83
  },
84
84
  {
@@ -204,67 +204,21 @@
204
204
  },
205
205
  {
206
206
  "cell_type": "markdown",
207
- "id": "48bcbe87",
207
+ "id": "9c2e5465",
208
208
  "metadata": {},
209
- "source": "[//]: # (cr:doc name='c05_top_shap_section' id=48bcbe87)\n## 5.2 Compute Per-Slice Top SHAP Drivers\n\nPicks the top `SHAP_PER_SLICE_K` accounts from each `(playbook, archetype, risk_tier)` slice — ranked by `expected_loss DESC, churn_probability DESC` over the snapshot just written — joins their latest gold-feature row, runs a single distributed linear-SHAP `select` (importance × (x − background_mean) using the frozen `shap_attribution.json` artifact from training), and reduces each row to its `SHAP_TOP_DRIVERS_PER_ROW` strongest drivers by `|shap_contribution|`. The result is merged into `top_shap_drivers` keyed on `(model_name, model_version, entity_id)`.\n\nThe L3 cohort view (`v_eligible_all_playbooks`) shows only rows that have these drivers populated — the per-slice K is the single cap controlling its size, replacing the previous defense-in-depth `slice_rank <= 500` SQL filter. The L1 portfolio matrix and L2 archetype rollup remain populated from the full eligibility snapshot, so totals and capacity widgets stay truthful regardless of the SHAP cap.\n\nSkipped automatically when no model URI / attribution artifact is reachable (local runs without a trained MLflow run) or when `SHAP_PER_SLICE_K = 0`."
210
- },
211
- {
212
- "cell_type": "code",
213
- "execution_count": null,
214
- "id": "6509814d",
215
- "metadata": {},
216
- "outputs": [],
217
209
  "source": [
218
- "# @cr:code name='compute_top_shap_drivers' id=6509814d\n",
219
- "\n",
220
- "from customer_retention.stages.causal import (\n",
221
- " TopDriversConfig,\n",
222
- " compute_and_write_top_shap_drivers,\n",
223
- ")\n",
224
- "\n",
225
- "top_drivers_result = None\n",
226
- "if spark is None:\n",
227
- " print(\"SKIPPED: no Spark session (Databricks-only cell)\")\n",
228
- "elif snapshot_result is None:\n",
229
- " print(\"SKIPPED: snapshot_result is None — 5.1 did not produce a run\")\n",
230
- "elif int(SHAP_PER_SLICE_K) <= 0:\n",
231
- " print(\"SKIPPED: SHAP_PER_SLICE_K == 0 (per-slice SHAP enrichment disabled)\")\n",
232
- "elif MODEL_URI is None:\n",
233
- " print(\"SKIPPED: MODEL_URI is None — local runs without an MLflow attribution artifact cannot replay SHAP\")\n",
234
- "elif not spark.catalog.tableExists(GOLD_FEATURES_FQN):\n",
235
- " print(f\"SKIPPED: {GOLD_FEATURES_FQN} does not exist — gold features required for SHAP replay\")\n",
236
- "else:\n",
237
- " top_drivers_cfg = TopDriversConfig(\n",
238
- " spark=spark,\n",
239
- " snapshot_table_fqn=ELIGIBILITY_SNAPSHOT_FQN,\n",
240
- " gold_features_fqn=GOLD_FEATURES_FQN,\n",
241
- " top_shap_drivers_fqn=TOP_SHAP_DRIVERS_FQN,\n",
242
- " model_name=MODEL_NAME,\n",
243
- " model_version=str(MODEL_VERSION),\n",
244
- " scoring_run_id=snapshot_result.scoring_run_id,\n",
245
- " as_of_date=snapshot_result.as_of_date,\n",
246
- " model_uri=MODEL_URI,\n",
247
- " per_slice_k=int(SHAP_PER_SLICE_K),\n",
248
- " top_drivers_per_row=int(SHAP_TOP_DRIVERS_PER_ROW),\n",
249
- " )\n",
250
- " top_drivers_result = compute_and_write_top_shap_drivers(top_drivers_cfg)\n",
251
- " print(top_drivers_result.summary())\n"
210
+ "[//]: # (cr:doc name='c05_write_run_context_section' id=9c2e5465)\n",
211
+ "## 5.2 Write Run Context (app masthead projection)\n"
252
212
  ]
253
213
  },
254
- {
255
- "cell_type": "markdown",
256
- "id": "834012b4",
257
- "metadata": {},
258
- "source": "[//]: # (cr:doc name='c05_write_run_context_section' id=834012b4)\n## 5.3 Write Run Context (app masthead projection)\n\nUpserts a single row into `{catalog}.{schema}.run_context` keyed on the snapshot's `scoring_run_id`. Reads the optional `project_context.yaml` from the run namespace to populate horizon, primary objective, and temporal posture; when the YAML is unreachable the writer still emits a row so the dashboard view always returns model metadata. The companion `v_run_context` view (published in 5.4) selects the row with the most recent `as_of_date`, so re-running this cell is an idempotent MERGE."
259
- },
260
214
  {
261
215
  "cell_type": "code",
262
216
  "execution_count": null,
263
- "id": "6b63ca6a",
217
+ "id": "9a423d80",
264
218
  "metadata": {},
265
219
  "outputs": [],
266
220
  "source": [
267
- "# @cr:code name='write_run_context' id=6b63ca6a\n",
221
+ "# @cr:code name='write_run_context' id=9a423d80\n",
268
222
  "from customer_retention.stages.causal import (\n",
269
223
  " from_project_context,\n",
270
224
  " write_run_context,\n",
@@ -305,7 +259,6 @@
305
259
  " from mlflow.models import Model as _MlflowModel\n",
306
260
  " _info = _MlflowModel.load(MODEL_URI)\n",
307
261
  " _flavors = list((_info.flavors or {}).keys())\n",
308
- " # Prefer the most specific flavor over ``python_function``.\n",
309
262
  " for preferred in (\"xgboost\", \"lightgbm\", \"catboost\", \"pytorch\", \"tensorflow\", \"sklearn\"):\n",
310
263
  " if preferred in _flavors:\n",
311
264
  " return preferred\n",
@@ -346,7 +299,10 @@
346
299
  "cell_type": "markdown",
347
300
  "id": "a6cbe88f",
348
301
  "metadata": {},
349
- "source": "[//]: # (cr:doc name='c05_publish_views_section' id=a6cbe88f)\n## 5.4 Publish Dashboard SQL Views"
302
+ "source": [
303
+ "[//]: # (cr:doc name='c05_publish_views_section' id=a6cbe88f)\n",
304
+ "## 5.3 Publish Dashboard SQL Views\n"
305
+ ]
350
306
  },
351
307
  {
352
308
  "cell_type": "code",
@@ -357,7 +313,6 @@
357
313
  "source": [
358
314
  "# @cr:code name='publish_dashboard_views' id=00cf348e\n",
359
315
  "from customer_retention.stages.causal.dashboard_views import (\n",
360
- " DASHBOARD_DEVIATION_VIEW_NAMES,\n",
361
316
  " DASHBOARD_VIEW_NAMES,\n",
362
317
  " publish_dashboard_views,\n",
363
318
  ")\n",
@@ -367,26 +322,20 @@
367
322
  "elif not spark.catalog.tableExists(ELIGIBILITY_SNAPSHOT_FQN):\n",
368
323
  " print(f\"SKIPPED: {ELIGIBILITY_SNAPSHOT_FQN} not populated yet\")\n",
369
324
  "else:\n",
370
- " # Pass composite_name so the per-run deviation views\n",
371
- " # (v_account_feature_deviation / _topn) are also published -- they\n",
372
- " # reference gold_features_<composite_name> and stay skipped when\n",
373
- " # composite_name is None.\n",
374
- " statements = publish_dashboard_views(\n",
375
- " spark, CATALOG, SCHEMA, composite_name=COMPOSITE_NAME or None\n",
376
- " )\n",
325
+ " statements = publish_dashboard_views(spark, CATALOG, SCHEMA)\n",
377
326
  " print(f\"Published {len(statements)} dashboard views:\")\n",
378
327
  " for view_name in DASHBOARD_VIEW_NAMES:\n",
379
- " print(f\" - {CATALOG}.{SCHEMA}.{view_name}\")\n",
380
- " if COMPOSITE_NAME:\n",
381
- " for view_name in DASHBOARD_DEVIATION_VIEW_NAMES:\n",
382
- " print(f\" - {CATALOG}.{SCHEMA}.{view_name}\")\n"
328
+ " print(f\" - {CATALOG}.{SCHEMA}.{view_name}\")\n"
383
329
  ]
384
330
  },
385
331
  {
386
332
  "cell_type": "markdown",
387
333
  "id": "fd17e5f0",
388
334
  "metadata": {},
389
- "source": "[//]: # (cr:doc name='c05_summary_section' id=fd17e5f0)\n## 5.5 Print Run Summary"
335
+ "source": [
336
+ "[//]: # (cr:doc name='c05_summary_section' id=fd17e5f0)\n",
337
+ "## 5.4 Print Run Summary\n"
338
+ ]
390
339
  },
391
340
  {
392
341
  "cell_type": "code",
@@ -397,7 +346,7 @@
397
346
  "source": [
398
347
  "# @cr:code name='print_run_summary' id=b2232bcf\n",
399
348
  "if spark is None or not spark.catalog.tableExists(ARCHETYPE_CATALOG_FQN):\n",
400
- " print(\"(no archetype_catalog yet \\u2014 run c01..c03 first)\")\n",
349
+ " print(\"(no archetype_catalog yet run c01..c03 first)\")\n",
401
350
  "else:\n",
402
351
  " counts = spark.sql(\n",
403
352
  " f\"SELECT status, COUNT(*) AS n FROM {ARCHETYPE_CATALOG_FQN} GROUP BY status\"\n",
@@ -407,30 +356,7 @@
407
356
  " print(f\" {row['status']}: {row['n']}\")\n",
408
357
  " print(f\"Model: {MODEL_NAME} v{MODEL_VERSION}\")\n",
409
358
  " if snapshot_result is not None:\n",
410
- " print(snapshot_result.summary())\n",
411
- " if top_drivers_result is not None:\n",
412
- " print(top_drivers_result.summary())\n",
413
- " # SHAP-readiness check for the dashboard's right panel. The default\n",
414
- " # profile (and the ECH/SPS overrides) render the SHAP drivers panel\n",
415
- " # from ``v_account_explanation.account_top_shap_features`` -- sourced\n",
416
- " # from ``top_shap_drivers`` (per-row) with a fallback to\n",
417
- " # ``archetype_catalog.top_shap_features`` (per-archetype). If the row\n",
418
- " # count below is zero, the dashboard will show no SHAP bars; rerun\n",
419
- " # 5.2 ``compute_top_shap_drivers`` first.\n",
420
- " if spark.catalog.tableExists(TOP_SHAP_DRIVERS_FQN) and snapshot_result is not None:\n",
421
- " shap_counts = spark.sql(\n",
422
- " f\"SELECT COUNT(*) AS n, COUNT(DISTINCT entity_id) AS entities \"\n",
423
- " f\"FROM {TOP_SHAP_DRIVERS_FQN} \"\n",
424
- " f\"WHERE model_name = '{MODEL_NAME}' \"\n",
425
- " f\" AND model_version = '{MODEL_VERSION}'\"\n",
426
- " ).collect()[0]\n",
427
- " print(\n",
428
- " f\"top_shap_drivers (model={MODEL_NAME} v{MODEL_VERSION}): \"\n",
429
- " f\"{shap_counts['n']:,} rows / {shap_counts['entities']:,} entities -- \"\n",
430
- " f\"{'ready' if shap_counts['entities'] > 0 else 'EMPTY (rerun 5.2)'}\"\n",
431
- " )\n",
432
- " else:\n",
433
- " print(\"top_shap_drivers: not populated -- rerun 5.2 to enable the dashboard SHAP panel\")\n"
359
+ " print(snapshot_result.summary())\n"
434
360
  ]
435
361
  },
436
362
  {
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "churnkit"
3
- version = "1.02.2a2"
3
+ version = "1.02.2a4"
4
4
  description = "Structured ML framework for customer churn prediction -- from exploration notebooks to production pipelines, locally or on Databricks."
5
5
  readme = "README.md"
6
6
  license = {text = "Apache-2.0"}
@@ -438,12 +438,25 @@ _C02_BACKFILL_PROSE_BODY = '''# Re-render `eligibility_rules_prose` for every ex
438
438
  # feature_population_stats sidecars existed stay NULL until we rewrite them.
439
439
  # Cycle 013 P4 surfaced exactly this gap; the backfill closes it idempotently.
440
440
  if is_databricks() and spark is not None and spark.catalog.tableExists(ELIGIBILITY_POLICY_FQN):
441
+ from customer_retention.analysis.auto_explorer.run_namespace import RunNamespace
441
442
  from customer_retention.stages.causal.interpretation import (
442
443
  backfill_eligibility_prose,
443
444
  )
444
445
 
446
+ # Resolve the namespace locally so this cell runs standalone — the
447
+ # derivation cell only defines `_enrich_ns` on the freshly-derive path,
448
+ # so a backfill-only rerun (active archetypes already exist) would
449
+ # otherwise NameError on the reference below.
450
+ _backfill_ns = globals().get("_enrich_ns")
451
+ if _backfill_ns is None:
452
+ try:
453
+ _backfill_ns = RunNamespace.from_env_or_latest(get_experiments_dir())
454
+ except Exception as _ns_exc:
455
+ print(f"Backfill namespace lookup failed ({type(_ns_exc).__name__}: {_ns_exc}) — proceeding without sidecar context.")
456
+ _backfill_ns = None
457
+
445
458
  _backfill = backfill_eligibility_prose(
446
- spark, ELIGIBILITY_POLICY_FQN, namespace=_enrich_ns,
459
+ spark, ELIGIBILITY_POLICY_FQN, namespace=_backfill_ns,
447
460
  )
448
461
  print(_backfill.summary())
449
462
  if _backfill.warnings:
@@ -17,7 +17,7 @@ Main module categories:
17
17
  llm_context, iteration)
18
18
  """
19
19
 
20
- __version__ = "1.02.2a2"
20
+ __version__ = "1.02.2a4"
21
21
 
22
22
  # Environment utilities (always available)
23
23
  from .core.compat import (
@@ -31,6 +31,13 @@ _VIEW_FILE_NAME = "dashboard_views.sql"
31
31
  _DEVIATION_BLOCK_OPEN = "-- @cr:deviation-block:open"
32
32
  _DEVIATION_BLOCK_CLOSE = "-- @cr:deviation-block:close"
33
33
 
34
+ # Sentinel-delimited block for v_feature_provenance — gated on the optional
35
+ # feature_meta + column_descriptions tables existing in UC. Older causal-track
36
+ # builds did not produce these tables; the view's CREATE OR REPLACE would fail
37
+ # at validation time on those clusters and take the whole publish call down.
38
+ _PROVENANCE_BLOCK_OPEN = "-- @cr:provenance-block:open"
39
+ _PROVENANCE_BLOCK_CLOSE = "-- @cr:provenance-block:close"
40
+
34
41
  DASHBOARD_VIEW_NAMES: tuple[str, ...] = (
35
42
  "v_ranked_at_risk_customers",
36
43
  "v_archetype_overview",
@@ -44,12 +51,14 @@ DASHBOARD_VIEW_NAMES: tuple[str, ...] = (
44
51
  "v_eligible_all_playbooks",
45
52
  "v_account_explanation",
46
53
  "v_run_context",
47
- "v_feature_provenance",
48
54
  )
49
55
  DASHBOARD_DEVIATION_VIEW_NAMES: tuple[str, ...] = (
50
56
  "v_account_feature_deviation",
51
57
  "v_account_feature_deviation_topn",
52
58
  )
59
+ DASHBOARD_PROVENANCE_VIEW_NAMES: tuple[str, ...] = (
60
+ "v_feature_provenance",
61
+ )
53
62
 
54
63
 
55
64
  def load_dashboard_view_sql() -> str:
@@ -82,25 +91,50 @@ def _strip_deviation_markers(sql_text: str) -> str:
82
91
  )
83
92
 
84
93
 
94
+ def _strip_provenance_block(sql_text: str) -> str:
95
+ """Drop the ``@cr:provenance-block`` section so the rendered SQL parses
96
+ on clusters that don't yet have ``feature_meta`` / ``column_descriptions``.
97
+ """
98
+ pattern = re.compile(
99
+ re.escape(_PROVENANCE_BLOCK_OPEN) + r".*?" + re.escape(_PROVENANCE_BLOCK_CLOSE),
100
+ re.DOTALL,
101
+ )
102
+ return pattern.sub("", sql_text)
103
+
104
+
105
+ def _strip_provenance_markers(sql_text: str) -> str:
106
+ return "\n".join(
107
+ line for line in sql_text.splitlines()
108
+ if line.strip() not in (_PROVENANCE_BLOCK_OPEN, _PROVENANCE_BLOCK_CLOSE)
109
+ )
110
+
111
+
85
112
  def render_dashboard_view_sql(
86
113
  catalog: str,
87
114
  schema: str,
88
115
  *,
89
116
  composite_name: Optional[str] = None,
117
+ include_provenance: bool = True,
90
118
  ) -> str:
91
119
  """Substitute ``{catalog}`` / ``{schema}`` (and optionally ``{composite_name}``)
92
120
  into the raw SQL template.
93
121
 
94
122
  When ``composite_name`` is omitted, the deviation views (which reference
95
123
  ``gold_features_{composite_name}``) are stripped from the output so the
96
- remaining DDL stays parseable. Existing callers that don't supply the
97
- parameter keep their previous behaviour.
124
+ remaining DDL stays parseable. ``include_provenance=False`` strips the
125
+ ``v_feature_provenance`` block used by the publisher when the optional
126
+ ``feature_meta`` / ``column_descriptions`` tables aren't materialized yet
127
+ so the view's CREATE OR REPLACE wouldn't validate.
98
128
  """
99
129
  text = load_dashboard_view_sql()
100
130
  if composite_name:
101
131
  text = _strip_deviation_markers(text).replace("{composite_name}", composite_name)
102
132
  else:
103
133
  text = _strip_deviation_block(text)
134
+ if include_provenance:
135
+ text = _strip_provenance_markers(text)
136
+ else:
137
+ text = _strip_provenance_block(text)
104
138
  return text.replace("{catalog}", catalog).replace("{schema}", schema)
105
139
 
106
140
 
@@ -230,6 +264,29 @@ def _try_materialize_population_stats_from_sidecar(
230
264
  return spark.catalog.tableExists(fqn)
231
265
 
232
266
 
267
+ _PROVENANCE_PREREQ_TABLES: tuple[str, ...] = (
268
+ "feature_meta",
269
+ "column_descriptions",
270
+ )
271
+
272
+
273
+ def _provenance_prerequisites_present(
274
+ spark: "SparkSession", catalog: str, schema: str
275
+ ) -> tuple[bool, list[str]]:
276
+ """Return ``(ok, missing)`` for tables ``v_feature_provenance`` reads.
277
+
278
+ Older causal-track builds did not write ``feature_meta`` /
279
+ ``column_descriptions`` — on those clusters the provenance view is
280
+ skipped so the rest of the dashboard publishes successfully.
281
+ """
282
+ missing: list[str] = []
283
+ for tbl in _PROVENANCE_PREREQ_TABLES:
284
+ fqn = f"{catalog}.{schema}.{tbl}"
285
+ if not spark.catalog.tableExists(fqn):
286
+ missing.append(fqn)
287
+ return (not missing), missing
288
+
289
+
233
290
  def _deviation_prerequisites_present(
234
291
  spark: "SparkSession", catalog: str, schema: str, composite_name: str
235
292
  ) -> tuple[bool, list[str]]:
@@ -296,7 +353,21 @@ def publish_dashboard_views(
296
353
  )
297
354
  effective_composite = None
298
355
 
299
- rendered = render_dashboard_view_sql(catalog, schema, composite_name=effective_composite)
356
+ include_provenance, prov_missing = _provenance_prerequisites_present(spark, catalog, schema)
357
+ if not include_provenance:
358
+ logger.warning(
359
+ "v_feature_provenance skipped: missing prerequisite table(s) %s; "
360
+ "publishing the rest of the dashboard. Re-run gold materialization "
361
+ "to populate feature_meta / column_descriptions and republish.",
362
+ ", ".join(prov_missing),
363
+ )
364
+
365
+ rendered = render_dashboard_view_sql(
366
+ catalog,
367
+ schema,
368
+ composite_name=effective_composite,
369
+ include_provenance=include_provenance,
370
+ )
300
371
  statements = split_view_statements(rendered)
301
372
  submitted: List[str] = []
302
373
  for stmt in statements:
@@ -811,6 +811,7 @@ FROM ranked
811
811
  WHERE deviation_rank <= 12;
812
812
  -- @cr:deviation-block:close
813
813
 
814
+ -- @cr:provenance-block:open
814
815
  -- ============================================================================
815
816
  -- 14. v_feature_provenance (per-feature lineage + business definition)
816
817
  -- ----------------------------------------------------------------------------
@@ -884,3 +885,4 @@ SELECT
884
885
  cd.source_column_defs
885
886
  FROM fm
886
887
  LEFT JOIN col_defs cd ON cd.feature_name = fm.feature_name;
888
+ -- @cr:provenance-block:close
File without changes
File without changes
File without changes
File without changes