churnkit 1.2.8a2__tar.gz → 1.2.8a3__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 (503) hide show
  1. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/PKG-INFO +1 -1
  2. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/10_spec_generation.ipynb +1 -1
  3. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/pyproject.toml +1 -1
  4. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/__init__.py +1 -1
  5. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/findings_parser.py +60 -2
  6. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/.gitignore +0 -0
  7. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/LICENSE +0 -0
  8. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/README.md +0 -0
  9. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/__init__.py +0 -0
  10. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/accounts_view.py +0 -0
  11. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/archetype_view.py +0 -0
  12. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/config.py +0 -0
  13. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/customer_profile.py +0 -0
  14. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/data.py +0 -0
  15. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/default_profile.css +0 -0
  16. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/default_profile.html +0 -0
  17. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/masthead.py +0 -0
  18. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/state.py +0 -0
  19. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/template.py +0 -0
  20. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/theme.css +0 -0
  21. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/apps/databricks_app/src/treemap.py +0 -0
  22. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/causal_notebooks/c01_publish_definitions.ipynb +0 -0
  23. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/causal_notebooks/c02_archetype_derivation.ipynb +0 -0
  24. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/causal_notebooks/c03_approval_gate.ipynb +0 -0
  25. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/causal_notebooks/c04_batch_inference.ipynb +0 -0
  26. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/causal_notebooks/c05_snapshot_and_dashboard.ipynb +0 -0
  27. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/constraints/.gitkeep +0 -0
  28. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/-1_sample_datasets.ipynb +0 -0
  29. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/00_start_here.ipynb +0 -0
  30. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/01_data_discovery.ipynb +0 -0
  31. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/01a_a_temporal_text_deep_dive.ipynb +0 -0
  32. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/01a_temporal_deep_dive.ipynb +0 -0
  33. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/01b_temporal_quality.ipynb +0 -0
  34. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/01c_temporal_patterns.ipynb +0 -0
  35. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/01d_event_aggregation.ipynb +0 -0
  36. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/02_source_integrity.ipynb +0 -0
  37. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/03_dataset_merge.ipynb +0 -0
  38. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/04_column_deep_dive.ipynb +0 -0
  39. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/04a_text_columns_deep_dive.ipynb +0 -0
  40. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/05_relationship_analysis.ipynb +0 -0
  41. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/06_feature_opportunities.ipynb +0 -0
  42. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/07_modeling_readiness.ipynb +0 -0
  43. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/08_baseline_experiments.ipynb +0 -0
  44. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/09_business_alignment.ipynb +0 -0
  45. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/11_scoring_validation.ipynb +0 -0
  46. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/exploration_notebooks/12_view_documentation.ipynb +0 -0
  47. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/.claude/settings.local.json +0 -0
  48. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/README.md +0 -0
  49. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/build_framework_phase_map.py +0 -0
  50. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/data/create_snapshot.py +0 -0
  51. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/data/generate_edi_ticketing_dataset.py +0 -0
  52. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/data/generate_retail_dataset.py +0 -0
  53. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/data/generate_test_data.py +0 -0
  54. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/data/migrate_parquet_to_delta.py +0 -0
  55. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/data/migrate_to_temporal.py +0 -0
  56. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/databricks/build_wheel.sh +0 -0
  57. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/databricks/capture_runtime.py +0 -0
  58. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/databricks/dbr_init.sh +0 -0
  59. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/databricks/deploy_dev.py +0 -0
  60. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/databricks/generate_constraints.py +0 -0
  61. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/databricks/notebook_setup.py +0 -0
  62. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/experiments/capture_notebook_outputs.py +0 -0
  63. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/experiments/cell_profiling.py +0 -0
  64. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/experiments/compare_exploration_runs.py +0 -0
  65. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/experiments/compare_versions.sh +0 -0
  66. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/experiments/patch_dataset_config.py +0 -0
  67. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/generate_requirements.py +0 -0
  68. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/build_causal_notebooks.py +0 -0
  69. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/clean_notebook_outputs.py +0 -0
  70. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/export_tutorial_html.py +0 -0
  71. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/init_project.py +0 -0
  72. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/migrate_notebook_cell_ids.py +0 -0
  73. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/plotly_image_preprocessor.py +0 -0
  74. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/run_exploration.py +0 -0
  75. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/sync_notebooks.py +0 -0
  76. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/tag_framework_cells.py +0 -0
  77. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/tag_markdown_cells.py +0 -0
  78. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/notebooks/test_notebooks.py +0 -0
  79. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/release.sh +0 -0
  80. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/seed_playbooks_volume.py +0 -0
  81. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/templates/tutorial_html/conf.json +0 -0
  82. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/templates/tutorial_html/index.html.j2 +0 -0
  83. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/scripts/update_notebook_paths.py +0 -0
  84. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/__init__.py +0 -0
  85. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/__init__.py +0 -0
  86. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/active_dataset_store.py +0 -0
  87. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/analysis_context.py +0 -0
  88. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/column_describer.py +0 -0
  89. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/dataset_fingerprinter.py +0 -0
  90. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/entity_timestamp_deriver.py +0 -0
  91. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/exploration_manager.py +0 -0
  92. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/explorer.py +0 -0
  93. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/field_availability_audit.py +0 -0
  94. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/findings.py +0 -0
  95. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/intent_defaults.py +0 -0
  96. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/key_resolver.py +0 -0
  97. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/layered_recommendations.py +0 -0
  98. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/objective_support_communicator.py +0 -0
  99. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/prediction_objective_detector.py +0 -0
  100. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/project_context.py +0 -0
  101. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/recommendation_builder.py +0 -0
  102. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/recommendations.py +0 -0
  103. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/run_namespace.py +0 -0
  104. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/sampling.py +0 -0
  105. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/schema_report.py +0 -0
  106. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/service_unit_detector.py +0 -0
  107. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/session.py +0 -0
  108. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/skip_logic.py +0 -0
  109. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/auto_explorer/snapshot_grid.py +0 -0
  110. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/__init__.py +0 -0
  111. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/ab_test_designer.py +0 -0
  112. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/fairness_analyzer.py +0 -0
  113. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/intervention_matcher.py +0 -0
  114. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/ratio_features.py +0 -0
  115. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/report_generator.py +0 -0
  116. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/risk_profile.py +0 -0
  117. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/business/roi_analyzer.py +0 -0
  118. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/__init__.py +0 -0
  119. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/calibration_analyzer.py +0 -0
  120. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/cv_analyzer.py +0 -0
  121. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/error_analyzer.py +0 -0
  122. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/exploration_ledger.py +0 -0
  123. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/feature_provenance.py +0 -0
  124. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/feature_stability.py +0 -0
  125. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/leakage_detector.py +0 -0
  126. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/model_diagnostics_report.py +0 -0
  127. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/noise_tester.py +0 -0
  128. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/overfitting_analyzer.py +0 -0
  129. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/parity_report.py +0 -0
  130. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/diagnostics/segment_analyzer.py +0 -0
  131. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/discovery/__init__.py +0 -0
  132. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/discovery/config_generator.py +0 -0
  133. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/discovery/discovery_flow.py +0 -0
  134. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/discovery/type_inferencer.py +0 -0
  135. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/interpretability/__init__.py +0 -0
  136. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/interpretability/cohort_analyzer.py +0 -0
  137. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/interpretability/counterfactual.py +0 -0
  138. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/interpretability/individual_explainer.py +0 -0
  139. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/interpretability/pdp_generator.py +0 -0
  140. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/interpretability/shap_explainer.py +0 -0
  141. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/jupyter_save_hook.py +0 -0
  142. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/notebook_html_exporter.py +0 -0
  143. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/notebook_progress.py +0 -0
  144. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/plotly_preprocessor.py +0 -0
  145. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/__init__.py +0 -0
  146. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/base.py +0 -0
  147. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/cleaning/__init__.py +0 -0
  148. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/cleaning/consistency.py +0 -0
  149. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/cleaning/deduplicate.py +0 -0
  150. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/cleaning/impute.py +0 -0
  151. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/cleaning/outlier.py +0 -0
  152. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/datetime/__init__.py +0 -0
  153. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/datetime/extract.py +0 -0
  154. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/encoding/__init__.py +0 -0
  155. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/encoding/categorical.py +0 -0
  156. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/pipeline.py +0 -0
  157. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/registry.py +0 -0
  158. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/selection/__init__.py +0 -0
  159. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/selection/drop_column.py +0 -0
  160. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/transform/__init__.py +0 -0
  161. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/transform/power.py +0 -0
  162. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/recommendations/transform/scale.py +0 -0
  163. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/visualization/__init__.py +0 -0
  164. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/visualization/attention_scorer.py +0 -0
  165. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/visualization/chart_builder.py +0 -0
  166. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/visualization/column_paginator.py +0 -0
  167. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/visualization/console.py +0 -0
  168. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/visualization/display.py +0 -0
  169. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/analysis/visualization/number_formatter.py +0 -0
  170. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/artifacts/__init__.py +0 -0
  171. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/artifacts/fit_artifact_registry.py +0 -0
  172. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/cli.py +0 -0
  173. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/__init__.py +0 -0
  174. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/__init__.py +0 -0
  175. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/bulk_profiling.py +0 -0
  176. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/cell_profiling_hooks.py +0 -0
  177. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/detection.py +0 -0
  178. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/ops.py +0 -0
  179. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/pandas_backend.py +0 -0
  180. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/remote_path.py +0 -0
  181. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/spark_backend.py +0 -0
  182. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/compat/timing.py +0 -0
  183. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/__init__.py +0 -0
  184. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/base.py +0 -0
  185. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/__init__.py +0 -0
  186. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/deployer.py +0 -0
  187. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/explainer.py +0 -0
  188. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/feature_eng.py +0 -0
  189. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/ingester.py +0 -0
  190. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/profiler.py +0 -0
  191. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/trainer.py +0 -0
  192. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/transformer.py +0 -0
  193. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/components/validator.py +0 -0
  194. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/enums.py +0 -0
  195. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/orchestrator.py +0 -0
  196. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/components/registry.py +0 -0
  197. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/config/__init__.py +0 -0
  198. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/config/column_config.py +0 -0
  199. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/config/experiments.py +0 -0
  200. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/config/pipeline_config.py +0 -0
  201. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/config/source_config.py +0 -0
  202. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/naming.py +0 -0
  203. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/utils/__init__.py +0 -0
  204. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/utils/leakage.py +0 -0
  205. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/utils/severity.py +0 -0
  206. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/core/utils/statistics.py +0 -0
  207. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/__init__.py +0 -0
  208. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/__init__.py +0 -0
  209. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/base.py +0 -0
  210. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/cell_builder.py +0 -0
  211. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/config.py +0 -0
  212. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/databricks_generator.py +0 -0
  213. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/local_generator.py +0 -0
  214. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/project_init.py +0 -0
  215. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/runner.py +0 -0
  216. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/scoring_replay.py +0 -0
  217. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/script_generator.py +0 -0
  218. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/__init__.py +0 -0
  219. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/base_stage.py +0 -0
  220. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/causal_setup_cell.py +0 -0
  221. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s01_ingestion.py +0 -0
  222. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s02_profiling.py +0 -0
  223. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s03_cleaning.py +0 -0
  224. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s04_transformation.py +0 -0
  225. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s05_feature_engineering.py +0 -0
  226. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s06_feature_selection.py +0 -0
  227. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s07_model_training.py +0 -0
  228. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s08_deployment.py +0 -0
  229. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s09_monitoring.py +0 -0
  230. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s10_batch_inference.py +0 -0
  231. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s11_feature_store.py +0 -0
  232. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s_c01_publish_definitions.py +0 -0
  233. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s_c02_archetype_derivation.py +0 -0
  234. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s_c03_approval_gate.py +0 -0
  235. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s_c04_batch_inference.py +0 -0
  236. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_generator/stages/s_c05_snapshot_and_dashboard.py +0 -0
  237. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_merge/__init__.py +0 -0
  238. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_merge/cli.py +0 -0
  239. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_merge/config_merger.py +0 -0
  240. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_merge/config_parser.py +0 -0
  241. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_merge/conflict.py +0 -0
  242. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_merge/merge_engine.py +0 -0
  243. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_merge/merge_report.py +0 -0
  244. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_sync/__init__.py +0 -0
  245. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_sync/cell_id_standardizer.py +0 -0
  246. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_sync/cell_types.py +0 -0
  247. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_sync/cli.py +0 -0
  248. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_sync/sync_engine.py +0 -0
  249. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/notebook_sync/sync_report.py +0 -0
  250. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/orchestration/__init__.py +0 -0
  251. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/orchestration/code_generator.py +0 -0
  252. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/orchestration/context.py +0 -0
  253. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/orchestration/data_materializer.py +0 -0
  254. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/orchestration/databricks_exporter.py +0 -0
  255. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/orchestration/doc_generator.py +0 -0
  256. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/__init__.py +0 -0
  257. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/databricks_generator.py +0 -0
  258. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/databricks_renderer.py +0 -0
  259. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/exploration_generator.py +0 -0
  260. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/generation_manifest.py +0 -0
  261. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/generator.py +0 -0
  262. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/gold_transform_applicator.py +0 -0
  263. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/llm_docs_generator.py +0 -0
  264. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/models.py +0 -0
  265. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/override_merge.py +0 -0
  266. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/protocols.py +0 -0
  267. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/renderer.py +0 -0
  268. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/pipeline_generator/user_extensions_emitter.py +0 -0
  269. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/spec_generator/__init__.py +0 -0
  270. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/generators/spec_generator/mlflow_pipeline_generator.py +0 -0
  271. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/__init__.py +0 -0
  272. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/__init__.py +0 -0
  273. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/base.py +0 -0
  274. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/factory.py +0 -0
  275. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/feature_store/__init__.py +0 -0
  276. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/feature_store/base.py +0 -0
  277. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/feature_store/databricks.py +0 -0
  278. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/feature_store/feast_adapter.py +0 -0
  279. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/feature_store/local.py +0 -0
  280. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/mlflow/__init__.py +0 -0
  281. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/mlflow/base.py +0 -0
  282. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/mlflow/databricks.py +0 -0
  283. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/mlflow/experiment_tracker.py +0 -0
  284. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/mlflow/local.py +0 -0
  285. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/storage/__init__.py +0 -0
  286. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/storage/base.py +0 -0
  287. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/storage/databricks.py +0 -0
  288. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/adapters/storage/local.py +0 -0
  289. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/databricks_init.py +0 -0
  290. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/databricks_job_capture.py +0 -0
  291. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/feature_store/__init__.py +0 -0
  292. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/feature_store/definitions.py +0 -0
  293. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/feature_store/manager.py +0 -0
  294. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/feature_store/registry.py +0 -0
  295. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/iteration/__init__.py +0 -0
  296. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/iteration/context.py +0 -0
  297. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/iteration/feedback_collector.py +0 -0
  298. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/iteration/orchestrator.py +0 -0
  299. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/iteration/recommendation_tracker.py +0 -0
  300. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/iteration/signals.py +0 -0
  301. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/llm_context/__init__.py +0 -0
  302. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/llm_context/context_builder.py +0 -0
  303. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/llm_context/prompts.py +0 -0
  304. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/requirements_generator.py +0 -0
  305. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/__init__.py +0 -0
  306. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/batch_integration.py +0 -0
  307. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/early_warning_model.py +0 -0
  308. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/event_schema.py +0 -0
  309. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/online_store_writer.py +0 -0
  310. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/realtime_scorer.py +0 -0
  311. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/trigger_engine.py +0 -0
  312. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/integrations/streaming/window_aggregator.py +0 -0
  313. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/__init__.py +0 -0
  314. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/api.py +0 -0
  315. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/dataset_resolution.py +0 -0
  316. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/decorator.py +0 -0
  317. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/flags.py +0 -0
  318. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/harvest.py +0 -0
  319. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/logging.py +0 -0
  320. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/persistence.py +0 -0
  321. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/registry.py +0 -0
  322. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/replay.py +0 -0
  323. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/summary.py +0 -0
  324. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/runtime/validation.py +0 -0
  325. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/__init__.py +0 -0
  326. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/__init__.py +0 -0
  327. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/approval_gate.py +0 -0
  328. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/clusterer.py +0 -0
  329. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/column_descriptions_writer.py +0 -0
  330. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/dashboard_profile_override.py +0 -0
  331. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/dashboard_views.py +0 -0
  332. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/delta_writer.py +0 -0
  333. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/derivation.py +0 -0
  334. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/feature_meta_writer.py +0 -0
  335. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/__init__.py +0 -0
  336. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/archetype_context.py +0 -0
  337. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/business_phrase.py +0 -0
  338. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/discovery.py +0 -0
  339. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/enrichment_pipeline.py +0 -0
  340. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/feature_meta_builder.py +0 -0
  341. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/llm_prompt.py +0 -0
  342. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/markdown_bootstrap.py +0 -0
  343. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/predicate_prose.py +0 -0
  344. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/prose_backfill.py +0 -0
  345. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/quantile_phrasing.py +0 -0
  346. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/interpretation/sidecars.py +0 -0
  347. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/llm_namer.py +0 -0
  348. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/playbook_loader.py +0 -0
  349. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/playbook_mapper.py +0 -0
  350. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/policy_loader.py +0 -0
  351. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/population_stats.py +0 -0
  352. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/predicate_compiler.py +0 -0
  353. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/rule_extractor.py +0 -0
  354. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/run_context_writer.py +0 -0
  355. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/schemas.py +0 -0
  356. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/seed_yamls/decision_policy.yaml +0 -0
  357. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/seed_yamls/response_schemas.yaml +0 -0
  358. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/seed_yamls/vocabularies.yaml +0 -0
  359. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/shap_runner.py +0 -0
  360. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/snapshot_writer.py +0 -0
  361. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/sql/dashboard_views.sql +0 -0
  362. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/causal/top_drivers_writer.py +0 -0
  363. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/cleaning/__init__.py +0 -0
  364. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/cleaning/base.py +0 -0
  365. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/cleaning/missing_handler.py +0 -0
  366. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/cleaning/outlier_handler.py +0 -0
  367. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/deployment/__init__.py +0 -0
  368. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/deployment/batch_scorer.py +0 -0
  369. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/deployment/champion_challenger.py +0 -0
  370. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/deployment/model_registry.py +0 -0
  371. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/deployment/retraining_trigger.py +0 -0
  372. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/__init__.py +0 -0
  373. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/behavioral_features.py +0 -0
  374. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/customer_segmentation.py +0 -0
  375. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/feature_definitions.py +0 -0
  376. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/feature_engineer.py +0 -0
  377. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/feature_manifest.py +0 -0
  378. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/feature_selector.py +0 -0
  379. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/interaction_features.py +0 -0
  380. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/features/temporal_features.py +0 -0
  381. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/ingestion/__init__.py +0 -0
  382. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/ingestion/load_result.py +0 -0
  383. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/ingestion/loaders.py +0 -0
  384. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/ingestion/source_registry.py +0 -0
  385. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/lifecycle/__init__.py +0 -0
  386. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/lifecycle/config.py +0 -0
  387. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/lifecycle/enrich.py +0 -0
  388. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/lifecycle/validation.py +0 -0
  389. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/__init__.py +0 -0
  390. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/baseline_trainer.py +0 -0
  391. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/cross_validator.py +0 -0
  392. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/data_splitter.py +0 -0
  393. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/feature_profile.py +0 -0
  394. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/feature_scaler.py +0 -0
  395. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/feature_spec.py +0 -0
  396. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/hyperparameter_tuner.py +0 -0
  397. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/imbalance_handler.py +0 -0
  398. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/mlflow_logger.py +0 -0
  399. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/model_comparator.py +0 -0
  400. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/model_evaluator.py +0 -0
  401. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/shap_attribution.py +0 -0
  402. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/spark_baseline_trainer.py +0 -0
  403. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/spark_classifier_wrapper.py +0 -0
  404. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/spark_feature_scaler.py +0 -0
  405. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/threshold_optimizer.py +0 -0
  406. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/modeling/training_preparator.py +0 -0
  407. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/monitoring/__init__.py +0 -0
  408. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/monitoring/alert_manager.py +0 -0
  409. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/monitoring/drift_detector.py +0 -0
  410. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/monitoring/performance_monitor.py +0 -0
  411. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/preprocessing/__init__.py +0 -0
  412. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/preprocessing/transformer_manager.py +0 -0
  413. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/__init__.py +0 -0
  414. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/categorical_distribution.py +0 -0
  415. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/categorical_target_analyzer.py +0 -0
  416. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/column_profiler.py +0 -0
  417. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/distribution_analysis.py +0 -0
  418. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/drift_detector.py +0 -0
  419. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/feature_capacity.py +0 -0
  420. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/pattern_analysis_config.py +0 -0
  421. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/profile_result.py +0 -0
  422. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/quality_checks.py +0 -0
  423. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/recommendation_filter.py +0 -0
  424. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/relationship_detector.py +0 -0
  425. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/relationship_recommender.py +0 -0
  426. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/report_generator.py +0 -0
  427. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/scd_analyzer.py +0 -0
  428. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/segment_analyzer.py +0 -0
  429. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/segment_aware_outlier.py +0 -0
  430. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/spark_segment_analyzer.py +0 -0
  431. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/spark_temporal_feature_analyzer.py +0 -0
  432. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/spark_temporal_feature_engineer.py +0 -0
  433. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/spark_time_window_aggregator.py +0 -0
  434. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/stats_helpers.py +0 -0
  435. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/target_level_analyzer.py +0 -0
  436. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/target_validator.py +0 -0
  437. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/temporal_analyzer.py +0 -0
  438. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/temporal_coverage.py +0 -0
  439. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/temporal_feature_analyzer.py +0 -0
  440. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/temporal_feature_engineer.py +0 -0
  441. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/temporal_pattern_analyzer.py +0 -0
  442. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/temporal_quality_checks.py +0 -0
  443. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/temporal_target_analyzer.py +0 -0
  444. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/text_embedder.py +0 -0
  445. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/text_processor.py +0 -0
  446. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/text_reducer.py +0 -0
  447. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/time_series_profiler.py +0 -0
  448. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/time_window_aggregator.py +0 -0
  449. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/type_detector.py +0 -0
  450. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/profiling/window_recommendation.py +0 -0
  451. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scd_history/__init__.py +0 -0
  452. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scd_history/augment.py +0 -0
  453. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scd_history/config.py +0 -0
  454. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scd_history/reconstruct.py +0 -0
  455. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scd_history/validation.py +0 -0
  456. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scoring/__init__.py +0 -0
  457. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scoring/batch_inference.py +0 -0
  458. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scoring/config.py +0 -0
  459. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scoring/data_loader.py +0 -0
  460. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scoring/exceptions.py +0 -0
  461. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/scoring/pipeline_discovery.py +0 -0
  462. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/__init__.py +0 -0
  463. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/access_guard.py +0 -0
  464. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/data_preparer.py +0 -0
  465. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/point_in_time_join.py +0 -0
  466. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/point_in_time_registry.py +0 -0
  467. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/scenario_detector.py +0 -0
  468. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/snapshot_manager.py +0 -0
  469. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/spark_temporal_merger.py +0 -0
  470. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/synthetic_coordinator.py +0 -0
  471. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/temporal_merger.py +0 -0
  472. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/timestamp_discovery.py +0 -0
  473. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/temporal/timestamp_manager.py +0 -0
  474. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/transformation/__init__.py +0 -0
  475. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/transformation/binary_handler.py +0 -0
  476. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/transformation/categorical_encoder.py +0 -0
  477. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/transformation/datetime_transformer.py +0 -0
  478. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/transformation/numeric_transformer.py +0 -0
  479. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/transformation/pipeline.py +0 -0
  480. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/__init__.py +0 -0
  481. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/adversarial_scoring_validator.py +0 -0
  482. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/business_sense_gate.py +0 -0
  483. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/data_quality_gate.py +0 -0
  484. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/data_validators.py +0 -0
  485. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/feature_quality_gate.py +0 -0
  486. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/gates.py +0 -0
  487. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/leakage_gate.py +0 -0
  488. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/model_validity_gate.py +0 -0
  489. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/pipeline_validation_runner.py +0 -0
  490. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/quality_scorer.py +0 -0
  491. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/rule_generator.py +0 -0
  492. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/scoring_pipeline_validator.py +0 -0
  493. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/stages/validation/timeseries_detector.py +0 -0
  494. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/transforms/__init__.py +0 -0
  495. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/transforms/artifact_store.py +0 -0
  496. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/transforms/executor.py +0 -0
  497. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/transforms/fitted.py +0 -0
  498. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/transforms/ops.py +0 -0
  499. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/src/customer_retention/transforms/spark_ops.py +0 -0
  500. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/tests/exploration_notebooks/test_distributed_dtype_safety.py +0 -0
  501. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/tests/exploration_notebooks/test_nb10_run_pipeline_ordering.py +0 -0
  502. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/tests/scripts/__init__.py +0 -0
  503. {churnkit-1.2.8a2 → churnkit-1.2.8a3}/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.8a2
3
+ Version: 1.2.8a3
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
@@ -991,7 +991,7 @@
991
991
  "tags": []
992
992
  },
993
993
  "outputs": [],
994
- "source": "# @cr:code name='run_pipeline' id=8b659505\nRUN_PIPELINE = True\n# Resume gate: skip any landing/bronze/silver/gold notebook whose output UC table\n# already exists. Operator-facing knob — flip to False for a forced full re-run\n# (e.g. when findings or generated code changed in a way that invalidates prior\n# tables). Training is never resume-skipped (no UC table to check; MLflow runs\n# are tracked separately).\nRESUME_FROM_EXISTING_TABLES = True\n\nif RUN_PIPELINE and GENERATION_TARGET == GenerationTarget.DATABRICKS:\n import json as _json\n import time as _time\n\n def _resume_table_for(stage, nb_name, catalog, schema):\n \"\"\"Resolve the UC table written by a generated stage notebook.\n\n Conventions established by the Databricks renderer:\n - landing/landing_<src> -> {cat}.{sch}.landing_<src>\n - bronze/bronze_entity_<src> -> {cat}.{sch}.bronze_entity_<src>\n - bronze/bronze_event_<src> -> {cat}.{sch}.bronze_entity_<src>_events (Shape A)\n - bronze/bronze_entity_<src>_aggregated -> same name as notebook\n - silver/silver_featureset_<CN> -> same name as notebook\n - gold/gold_features_<CN> -> same name as notebook\n - training/* -> None (no UC table)\n \"\"\"\n base = f\"{catalog}.{schema}\"\n if stage == \"landing\":\n return f\"{base}.{nb_name}\"\n if stage == \"bronze\":\n if nb_name.startswith(\"bronze_event_\"):\n source = nb_name[len(\"bronze_event_\"):]\n return f\"{base}.bronze_entity_{source}_events\"\n return f\"{base}.{nb_name}\"\n if stage in (\"silver\", \"gold\"):\n return f\"{base}.{nb_name}\"\n return None\n\n print(f\"Databricks pipeline: {output_dir}\\n\")\n if RESUME_FROM_EXISTING_TABLES:\n print(\"[RESUME] table-existence resume enabled — stages with existing UC tables will be skipped\\n\")\n spark.sql(f\"CREATE SCHEMA IF NOT EXISTS {DATABRICKS_CATALOG}.{DATABRICKS_SCHEMA}\")\n _ns_params = {\"experiments_dir\": str(_namespace.root), \"run_id\": _namespace.run_id} if _namespace else {}\n _stages = [\"landing\", \"bronze\", \"silver\", \"gold\", \"training\"]\n _total_start = _time.time()\n _training_results = None\n _silver_results = None\n _skipped_count = 0\n for _stage in _stages:\n _stage_dir = output_dir / _stage\n if not _stage_dir.exists():\n continue\n if _stage == \"bronze\":\n # (retires NB10 patch §2.11): events must run before\n # entities so the aggregator inputs exist when consumers try\n # to read them. Default sorted() puts `bronze_entity_*`\n # before `bronze_event_*` (i < v) and raises\n # [TABLE_OR_VIEW_NOT_FOUND] on the first aggregated bronze.\n _notebooks = sorted(\n (f.stem for f in _stage_dir.iterdir() if f.suffix == \".py\"),\n key=lambda n: (0 if n.startswith(\"bronze_event_\") else 1, n),\n )\n print(f\"[bronze order] {len(_notebooks)} notebooks; events first:\")\n for _n in _notebooks[:6]:\n print(f\" {_n}\")\n if len(_notebooks) > 6:\n print(f\" ... + {len(_notebooks) - 6} more\")\n else:\n _notebooks = sorted(f.stem for f in _stage_dir.iterdir() if f.suffix == \".py\")\n for _nb in _notebooks:\n _path = str(output_dir / _stage / _nb)\n\n if RESUME_FROM_EXISTING_TABLES:\n _expected = _resume_table_for(_stage, _nb, DATABRICKS_CATALOG, DATABRICKS_SCHEMA)\n if _expected and spark.catalog.tableExists(_expected):\n print(f\"[SKIP] {_stage.upper()}/{_nb}: {_expected} already exists\")\n _skipped_count += 1\n continue\n\n _start = _time.time()\n _result = dbutils.notebook.run(_path, 86400, _ns_params)\n _elapsed = _time.time() - _start\n print(f\"[{_stage.upper()}] {_nb}: {_elapsed:.1f}s\")\n # Generated stage notebooks structured-exit on top-level exceptions\n # with {\"status\":\"FAILED\",...}; raise so the pipeline halts at the\n # failed stage instead of cascading opaque downstream errors.\n _parsed = None\n if _result:\n try:\n _parsed = _json.loads(_result)\n except (ValueError, TypeError):\n if _stage in (\"training\", \"silver\"):\n print(f\" result: {_result}\")\n if isinstance(_parsed, dict) and _parsed.get(\"status\") == \"FAILED\":\n raise RuntimeError(\n f\"[{_stage.upper()}] {_nb} failed: \"\n f\"{_parsed.get('error_type', '?')}: {_parsed.get('error_message', '')} \"\n \"(see spawned-notebook output for full traceback)\"\n )\n if isinstance(_parsed, dict) and _stage == \"training\":\n _training_results = _parsed\n elif isinstance(_parsed, dict) and _stage == \"silver\":\n _silver_results = _parsed\n print(f\"\\nTotal: {_time.time() - _total_start:.1f}s ({_skipped_count} stages skipped via resume)\")\n\n _ns = _namespace\n if _ns is not None:\n _bronze_path = _ns.bronze_metadata_path\n if _bronze_path.exists():\n _bm = _json.loads(_bronze_path.read_text())\n print(\"\\n\" + \"=\" * 60)\n print(\"BRONZE SUMMARY\")\n print(\"=\" * 60)\n print(f\"Sources: {_bm.get('total_sources', 0)}\")\n for _src, _info in _bm.get(\"sources\", {}).items():\n if isinstance(_info, dict):\n print(f\" {_src}: {_info.get('rows', '?'):,} rows, {_info.get('columns', '?')} columns\")\n else:\n print(f\" {_src}: {_info}\")\n\n _gold_path = _ns.gold_metadata_path\n if _gold_path.exists():\n _gm = _json.loads(_gold_path.read_text())\n print(\"\\n\" + \"=\" * 60)\n print(\"GOLD SUMMARY\")\n print(\"=\" * 60)\n print(f\"Rows: {_gm.get('rows', '?'):,}, Columns: {_gm.get('columns', '?')}\")\n if \"feature_count\" in _gm:\n print(f\"Features: {_gm['feature_count']}\")\n if \"feature_version\" in _gm:\n print(f\"Version: {_gm['feature_version']}\")\n if \"elapsed_seconds\" in _gm:\n print(f\"Elapsed: {_gm['elapsed_seconds']}s\")\n\n if _silver_results:\n print(\"\\n\" + \"=\" * 60)\n print(\"SILVER RESULTS\")\n print(\"=\" * 60)\n print(f\"Rows: {_silver_results.get('rows', '?'):,}, Columns: {_silver_results.get('columns', '?')}\")\n _es = _silver_results.get(\"elapsed_seconds\", {})\n if _es:\n print(\"\\nStage timings:\")\n for _name in (\"load_bronze\", \"merge_sources\", \"apply_derived\", \"holdout_mask\", \"delta_write\", \"optimize\", \"total\"):\n if _name in _es:\n print(f\" {_name:<18} {_es[_name]:>8.1f}s\")\n _mb = _silver_results.get(\"merge_breakdown\")\n if _mb:\n print(\"\\nMerge breakdown:\")\n print(f\" Spine: {_mb.get('spine_rows', 0):,} rows = {_mb.get('spine_entities', 0):,} entities x {_mb.get('spine_dates', 0)} dates ({_mb.get('spine_stats_seconds', 0):.1f}s)\")\n print(f\" Checkpoints: {_mb.get('checkpoint_count', 0)} ({_mb.get('checkpoint_seconds', 0):.1f}s)\")\n print(f\" Validation: {_mb.get('validation_seconds', 0):.1f}s\")\n print(f\" Merge total: {_mb.get('merge_total_seconds', 0):.1f}s\")\n _spd = _mb.get(\"seconds_per_dataset\", {})\n _cpd = _mb.get(\"columns_per_dataset\", {})\n print(\"\\nPer-source merge timing:\")\n _ranked = sorted(_mb.get(\"datasets_merged\", []), key=lambda n: _spd.get(n, 0), reverse=True)\n for _name in _ranked:\n print(f\" {_name:<40} {_spd.get(_name, 0):>8.1f}s (+{_cpd.get(_name, 0)} cols)\")\n\n if _training_results:\n print(\"\\n\" + \"=\" * 60)\n print(\"TRAINING RESULTS\")\n print(\"=\" * 60)\n if \"gold_data\" in _training_results:\n _gd = _training_results[\"gold_data\"]\n print(f\"\\nGold data: {_gd.get('rows', '?'):,} rows, {_gd.get('columns', '?')} columns\")\n print(f\"Column types: {_gd.get('column_types', {})}\")\n if \"feature_count\" in _training_results:\n print(f\"Features: {_training_results['feature_count']}\")\n if \"split\" in _training_results:\n _sp = _training_results[\"split\"]\n print(f\"\\nSplit: train={_sp.get('train_count', '?'):,}, test={_sp.get('test_count', '?'):,}\")\n print(f\"Cutoff date: {_sp.get('cutoff_date', 'N/A')}\")\n if \"label_distribution\" in _training_results:\n print(f\"Label distribution: {_training_results['label_distribution']}\")\n _fp = _training_results.get(\"feature_profile\", {})\n if _fp:\n print(f\"\\nFeature profile: production={_fp.get('production_features', '?')}, exploration={_fp.get('exploration_features', '?')}\")\n _disc = _fp.get(\"discrepancies\", [])\n if _disc:\n print(f\"WARNING: {len(_disc)} feature discrepancies vs exploration:\")\n for _d in _disc:\n print(f\" {_d}\")\n elif \"exploration_features\" in _fp:\n print(\"Feature profile matches exploration\")\n _excl = _fp.get(\"excluded_details\", {})\n if _excl:\n print(f\"\\nExclusion details ({len(_excl)} columns):\")\n for _col, _reason in sorted(_excl.items()):\n print(f\" {_col}: {_reason}\")\n if \"models\" in _training_results:\n print(\"\\nModel Results:\")\n print(f\"{'Model':<25} {'AUC':>8} {'PR-AUC':>8} {'F1':>8}\")\n print(\"-\" * 53)\n for _name, _metrics in _training_results[\"models\"].items():\n print(f\"{_name:<25} {_metrics.get('roc_auc', 0):.4f} {_metrics.get('pr_auc', 0):.4f} {_metrics.get('f1', 0):.4f}\")\n if \"best_model\" in _training_results:\n print(f\"\\nBest: {_training_results['best_model']} (AUC={_training_results.get('best_roc_auc', 0):.4f})\")\n\nelif RUN_PIPELINE and GENERATION_TARGET == GenerationTarget.LOCAL_FEAST_MLFLOW:\n import subprocess\n import sys\n runner_path = output_dir / \"pipeline_runner.py\"\n if runner_path.exists():\n print(f\"Running: python {runner_path.name}\")\n print(\"Pipeline: Landing -> Bronze -> Silver -> Gold -> Training...\\n\")\n proc = subprocess.Popen(\n [sys.executable, \"-u\", \"pipeline_runner.py\"],\n cwd=str(output_dir.resolve()),\n stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True,\n )\n for line in proc.stdout:\n print(line, end=\"\", flush=True)\n if proc.wait() != 0:\n raise subprocess.CalledProcessError(proc.returncode, \"pipeline_runner.py\")\n else:\n print(\"pipeline_runner.py not found. Generate first by running cells above.\")\n\nelse:\n print(\"To run the complete pipeline:\")\n print(f\"\\n cd {output_dir}\")\n print(\" python pipeline_runner.py\")"
994
+ "source": "# @cr:code name='run_pipeline' id=8b659505\nRUN_PIPELINE = True\n# Resume gate: skip any landing/bronze/silver/gold notebook whose output UC table\n# already exists. Operator-facing knob — flip to False for a forced full re-run\n# (e.g. when findings or generated code changed in a way that invalidates prior\n# tables). Training is never resume-skipped (no UC table to check; MLflow runs\n# are tracked separately).\nRESUME_FROM_EXISTING_TABLES = True\n\ndef _fmt_int(value, default=\"?\"):\n # Stage metadata writers can emit `null` for row counts when the post-write\n # readback is skipped (a wide-table perf knob); `:,` format on None/str\n # raises TypeError. Wrap every `:,`-formatted summary read.\n if isinstance(value, bool) or value is None:\n return default\n if isinstance(value, (int, float)):\n return f\"{value:,}\"\n return default\n\nif RUN_PIPELINE and GENERATION_TARGET == GenerationTarget.DATABRICKS:\n import json as _json\n import time as _time\n\n def _resume_table_for(stage, nb_name, catalog, schema):\n \"\"\"Resolve the UC table written by a generated stage notebook.\n\n Conventions established by the Databricks renderer:\n - landing/landing_<src> -> {cat}.{sch}.landing_<src>\n - bronze/bronze_entity_<src> -> {cat}.{sch}.bronze_entity_<src>\n - bronze/bronze_event_<src> -> {cat}.{sch}.bronze_entity_<src>_events (Shape A)\n - bronze/bronze_entity_<src>_aggregated -> same name as notebook\n - silver/silver_featureset_<CN> -> same name as notebook\n - gold/gold_features_<CN> -> same name as notebook\n - training/* -> None (no UC table)\n \"\"\"\n base = f\"{catalog}.{schema}\"\n if stage == \"landing\":\n return f\"{base}.{nb_name}\"\n if stage == \"bronze\":\n if nb_name.startswith(\"bronze_event_\"):\n source = nb_name[len(\"bronze_event_\"):]\n return f\"{base}.bronze_entity_{source}_events\"\n return f\"{base}.{nb_name}\"\n if stage in (\"silver\", \"gold\"):\n return f\"{base}.{nb_name}\"\n return None\n\n print(f\"Databricks pipeline: {output_dir}\\n\")\n if RESUME_FROM_EXISTING_TABLES:\n print(\"[RESUME] table-existence resume enabled — stages with existing UC tables will be skipped\\n\")\n spark.sql(f\"CREATE SCHEMA IF NOT EXISTS {DATABRICKS_CATALOG}.{DATABRICKS_SCHEMA}\")\n _ns_params = {\"experiments_dir\": str(_namespace.root), \"run_id\": _namespace.run_id} if _namespace else {}\n _stages = [\"landing\", \"bronze\", \"silver\", \"gold\", \"training\"]\n _total_start = _time.time()\n _training_results = None\n _silver_results = None\n _skipped_count = 0\n for _stage in _stages:\n _stage_dir = output_dir / _stage\n if not _stage_dir.exists():\n continue\n if _stage == \"bronze\":\n # (retires NB10 patch §2.11): events must run before\n # entities so the aggregator inputs exist when consumers try\n # to read them. Default sorted() puts `bronze_entity_*`\n # before `bronze_event_*` (i < v) and raises\n # [TABLE_OR_VIEW_NOT_FOUND] on the first aggregated bronze.\n _notebooks = sorted(\n (f.stem for f in _stage_dir.iterdir() if f.suffix == \".py\"),\n key=lambda n: (0 if n.startswith(\"bronze_event_\") else 1, n),\n )\n print(f\"[bronze order] {len(_notebooks)} notebooks; events first:\")\n for _n in _notebooks[:6]:\n print(f\" {_n}\")\n if len(_notebooks) > 6:\n print(f\" ... + {len(_notebooks) - 6} more\")\n else:\n _notebooks = sorted(f.stem for f in _stage_dir.iterdir() if f.suffix == \".py\")\n for _nb in _notebooks:\n _path = str(output_dir / _stage / _nb)\n\n if RESUME_FROM_EXISTING_TABLES:\n _expected = _resume_table_for(_stage, _nb, DATABRICKS_CATALOG, DATABRICKS_SCHEMA)\n if _expected and spark.catalog.tableExists(_expected):\n print(f\"[SKIP] {_stage.upper()}/{_nb}: {_expected} already exists\")\n _skipped_count += 1\n continue\n\n _start = _time.time()\n _result = dbutils.notebook.run(_path, 86400, _ns_params)\n _elapsed = _time.time() - _start\n print(f\"[{_stage.upper()}] {_nb}: {_elapsed:.1f}s\")\n # Generated stage notebooks structured-exit on top-level exceptions\n # with {\"status\":\"FAILED\",...}; raise so the pipeline halts at the\n # failed stage instead of cascading opaque downstream errors.\n _parsed = None\n if _result:\n try:\n _parsed = _json.loads(_result)\n except (ValueError, TypeError):\n if _stage in (\"training\", \"silver\"):\n print(f\" result: {_result}\")\n if isinstance(_parsed, dict) and _parsed.get(\"status\") == \"FAILED\":\n raise RuntimeError(\n f\"[{_stage.upper()}] {_nb} failed: \"\n f\"{_parsed.get('error_type', '?')}: {_parsed.get('error_message', '')} \"\n \"(see spawned-notebook output for full traceback)\"\n )\n if isinstance(_parsed, dict) and _stage == \"training\":\n _training_results = _parsed\n elif isinstance(_parsed, dict) and _stage == \"silver\":\n _silver_results = _parsed\n print(f\"\\nTotal: {_time.time() - _total_start:.1f}s ({_skipped_count} stages skipped via resume)\")\n\n _ns = _namespace\n if _ns is not None:\n _bronze_path = _ns.bronze_metadata_path\n if _bronze_path.exists():\n _bm = _json.loads(_bronze_path.read_text())\n print(\"\\n\" + \"=\" * 60)\n print(\"BRONZE SUMMARY\")\n print(\"=\" * 60)\n print(f\"Sources: {_bm.get('total_sources', 0)}\")\n for _src, _info in _bm.get(\"sources\", {}).items():\n if isinstance(_info, dict):\n print(f\" {_src}: {_fmt_int(_info.get('rows'))} rows, {_info.get('columns', '?')} columns\")\n else:\n print(f\" {_src}: {_info}\")\n\n _gold_path = _ns.gold_metadata_path\n if _gold_path.exists():\n _gm = _json.loads(_gold_path.read_text())\n print(\"\\n\" + \"=\" * 60)\n print(\"GOLD SUMMARY\")\n print(\"=\" * 60)\n print(f\"Rows: {_fmt_int(_gm.get('rows'))}, Columns: {_gm.get('columns', '?')}\")\n if \"feature_count\" in _gm:\n print(f\"Features: {_gm['feature_count']}\")\n if \"feature_version\" in _gm:\n print(f\"Version: {_gm['feature_version']}\")\n if \"elapsed_seconds\" in _gm:\n print(f\"Elapsed: {_gm['elapsed_seconds']}s\")\n\n if _silver_results:\n print(\"\\n\" + \"=\" * 60)\n print(\"SILVER RESULTS\")\n print(\"=\" * 60)\n print(f\"Rows: {_fmt_int(_silver_results.get('rows'))}, Columns: {_silver_results.get('columns', '?')}\")\n _es = _silver_results.get(\"elapsed_seconds\", {})\n if _es:\n print(\"\\nStage timings:\")\n for _name in (\"load_bronze\", \"merge_sources\", \"apply_derived\", \"holdout_mask\", \"delta_write\", \"optimize\", \"total\"):\n if _name in _es:\n print(f\" {_name:<18} {_es[_name]:>8.1f}s\")\n _mb = _silver_results.get(\"merge_breakdown\")\n if _mb:\n print(\"\\nMerge breakdown:\")\n print(f\" Spine: {_fmt_int(_mb.get('spine_rows'), '0')} rows = {_fmt_int(_mb.get('spine_entities'), '0')} entities x {_mb.get('spine_dates', 0)} dates ({_mb.get('spine_stats_seconds', 0):.1f}s)\")\n print(f\" Checkpoints: {_mb.get('checkpoint_count', 0)} ({_mb.get('checkpoint_seconds', 0):.1f}s)\")\n print(f\" Validation: {_mb.get('validation_seconds', 0):.1f}s\")\n print(f\" Merge total: {_mb.get('merge_total_seconds', 0):.1f}s\")\n _spd = _mb.get(\"seconds_per_dataset\", {})\n _cpd = _mb.get(\"columns_per_dataset\", {})\n print(\"\\nPer-source merge timing:\")\n _ranked = sorted(_mb.get(\"datasets_merged\", []), key=lambda n: _spd.get(n, 0), reverse=True)\n for _name in _ranked:\n print(f\" {_name:<40} {_spd.get(_name, 0):>8.1f}s (+{_cpd.get(_name, 0)} cols)\")\n\n if _training_results:\n print(\"\\n\" + \"=\" * 60)\n print(\"TRAINING RESULTS\")\n print(\"=\" * 60)\n if \"gold_data\" in _training_results:\n _gd = _training_results[\"gold_data\"]\n print(f\"\\nGold data: {_fmt_int(_gd.get('rows'))} rows, {_gd.get('columns', '?')} columns\")\n print(f\"Column types: {_gd.get('column_types', {})}\")\n if \"feature_count\" in _training_results:\n print(f\"Features: {_training_results['feature_count']}\")\n if \"split\" in _training_results:\n _sp = _training_results[\"split\"]\n print(f\"\\nSplit: train={_fmt_int(_sp.get('train_count'))}, test={_fmt_int(_sp.get('test_count'))}\")\n print(f\"Cutoff date: {_sp.get('cutoff_date', 'N/A')}\")\n if \"label_distribution\" in _training_results:\n print(f\"Label distribution: {_training_results['label_distribution']}\")\n _fp = _training_results.get(\"feature_profile\", {})\n if _fp:\n print(f\"\\nFeature profile: production={_fp.get('production_features', '?')}, exploration={_fp.get('exploration_features', '?')}\")\n _disc = _fp.get(\"discrepancies\", [])\n if _disc:\n print(f\"WARNING: {len(_disc)} feature discrepancies vs exploration:\")\n for _d in _disc:\n print(f\" {_d}\")\n elif \"exploration_features\" in _fp:\n print(\"Feature profile matches exploration\")\n _excl = _fp.get(\"excluded_details\", {})\n if _excl:\n print(f\"\\nExclusion details ({len(_excl)} columns):\")\n for _col, _reason in sorted(_excl.items()):\n print(f\" {_col}: {_reason}\")\n if \"models\" in _training_results:\n print(\"\\nModel Results:\")\n print(f\"{'Model':<25} {'AUC':>8} {'PR-AUC':>8} {'F1':>8}\")\n print(\"-\" * 53)\n for _name, _metrics in _training_results[\"models\"].items():\n print(f\"{_name:<25} {_metrics.get('roc_auc', 0):.4f} {_metrics.get('pr_auc', 0):.4f} {_metrics.get('f1', 0):.4f}\")\n if \"best_model\" in _training_results:\n print(f\"\\nBest: {_training_results['best_model']} (AUC={_training_results.get('best_roc_auc', 0):.4f})\")\n\nelif RUN_PIPELINE and GENERATION_TARGET == GenerationTarget.LOCAL_FEAST_MLFLOW:\n import subprocess\n import sys\n runner_path = output_dir / \"pipeline_runner.py\"\n if runner_path.exists():\n print(f\"Running: python {runner_path.name}\")\n print(\"Pipeline: Landing -> Bronze -> Silver -> Gold -> Training...\\n\")\n proc = subprocess.Popen(\n [sys.executable, \"-u\", \"pipeline_runner.py\"],\n cwd=str(output_dir.resolve()),\n stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True,\n )\n for line in proc.stdout:\n print(line, end=\"\", flush=True)\n if proc.wait() != 0:\n raise subprocess.CalledProcessError(proc.returncode, \"pipeline_runner.py\")\n else:\n print(\"pipeline_runner.py not found. Generate first by running cells above.\")\n\nelse:\n print(\"To run the complete pipeline:\")\n print(f\"\\n cd {output_dir}\")\n print(\" python pipeline_runner.py\")"
995
995
  },
996
996
  {
997
997
  "cell_type": "markdown",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "churnkit"
3
- version = "1.02.8a2"
3
+ version = "1.02.8a3"
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"}
@@ -17,7 +17,7 @@ Main module categories:
17
17
  llm_context, iteration)
18
18
  """
19
19
 
20
- __version__ = "1.02.8a2"
20
+ __version__ = "1.02.8a3"
21
21
 
22
22
  # Environment utilities (always available)
23
23
  from .core.compat import (
@@ -522,7 +522,8 @@ class FindingsParser:
522
522
 
523
523
  def _index_raw_source_columns(self, discovered_events: Dict[str, ExplorationFindings]) -> None:
524
524
  for name, findings in discovered_events.items():
525
- self._raw_source_columns[name] = set(findings.columns.keys())
525
+ cols = set(findings.columns.keys())
526
+ self._raw_source_columns.setdefault(name, set()).update(cols)
526
527
  per_col: Dict[str, List[str]] = {}
527
528
  for col_name, col_finding in findings.columns.items():
528
529
  tm = getattr(col_finding, "type_metrics", None) or {}
@@ -537,7 +538,15 @@ class FindingsParser:
537
538
  for c in top_cats
538
539
  ]
539
540
  if per_col:
540
- self._value_counts_by_source[name] = per_col
541
+ # Merge — don't replace. Two findings files (entity-level and
542
+ # pre-aggregated) can both register columns under the same
543
+ # source name; the second pass otherwise clobbers the first
544
+ # and loses raw event-stream value-counts (e.g. event_type)
545
+ # that only the entity-level file carries.
546
+ bucket = self._value_counts_by_source.setdefault(name, {})
547
+ for col, vals in per_col.items():
548
+ if col not in bucket:
549
+ bucket[col] = vals
541
550
  self._augment_value_counts_from_spec()
542
551
 
543
552
  _SPEC_TRAILING_WINDOW_RE = re.compile(r"^(?P<head>.+)_count_(?:\d+[dhw]|all_time)$")
@@ -1211,6 +1220,7 @@ class FindingsParser:
1211
1220
  source_findings: Optional[Dict[str, "ExplorationFindings"]] = None,
1212
1221
  ) -> None:
1213
1222
  self._apply_bronze_recommendations(config, registry)
1223
+ self._apply_registry_bronze_aggregations(config, registry)
1214
1224
  self._apply_imbalance_recommendations(config, registry)
1215
1225
  self._apply_silver_recommendations(config, registry)
1216
1226
  zero_inflation_opt_in_prefixes = self._collect_zero_inflation_opt_in_prefixes(
@@ -1596,6 +1606,43 @@ class FindingsParser:
1596
1606
  return name
1597
1607
  return None
1598
1608
 
1609
+ def _apply_registry_bronze_aggregations(
1610
+ self, config: PipelineConfig, registry: RecommendationRegistry
1611
+ ) -> None:
1612
+ """Promote ``registry.bronze_aggregations`` onto event_cfg for sources
1613
+ the operator didn't restate in NB10's ``BRONZE_AGGREGATIONS`` dict.
1614
+
1615
+ Without this, a per-source declaration made via
1616
+ ``add_bronze_value_counts`` / ``add_bronze_per_grid_date_mode`` during
1617
+ exploration is silently ignored at codegen unless the operator
1618
+ manually echoes it into NB10. Recs that depend on the per-value count
1619
+ shape (e.g. ``subscription_terminate_to_start_ratio_*``) get dropped.
1620
+ Operator overrides win on conflict; only un-overridden sources are
1621
+ filled in here.
1622
+ """
1623
+ ba = getattr(registry, "bronze_aggregations", None) or {}
1624
+ if not isinstance(ba, dict) or not ba:
1625
+ return
1626
+ if self._bronze_aggregation_overrides is None:
1627
+ self._bronze_aggregation_overrides = {}
1628
+ for src, entry in ba.items():
1629
+ if src in self._bronze_aggregation_overrides:
1630
+ continue
1631
+ if src not in config.bronze_event:
1632
+ continue
1633
+ if not isinstance(entry, dict):
1634
+ continue
1635
+ recognized = {
1636
+ k: v for k, v in entry.items()
1637
+ if k in _RECOGNIZED_BRONZE_OVERRIDE_KEYS
1638
+ }
1639
+ if not recognized:
1640
+ continue
1641
+ self._bronze_aggregation_overrides[src] = recognized
1642
+ self._apply_bronze_aggregation_overrides(
1643
+ config.bronze_event[src], src,
1644
+ )
1645
+
1599
1646
  def _apply_bronze_recommendations(self, config: PipelineConfig, registry: RecommendationRegistry) -> None:
1600
1647
  sources_to_process = dict(registry.sources)
1601
1648
  if not sources_to_process and hasattr(registry, "bronze") and registry.bronze is not None:
@@ -2480,6 +2527,17 @@ class FindingsParser:
2480
2527
  for src in dd.source_columns:
2481
2528
  for suffix in ("_delta_hours", "_hour", "_dow", "_is_weekend"):
2482
2529
  columns.add(f"{src}{suffix}")
2530
+ # The runtime aggregator computes datetime-derived features per row
2531
+ # (`compute_temporal_features`) and then runs them through the same
2532
+ # window aggregation as numeric `value_columns`. Mirror that fan-out
2533
+ # so silver_derived recs referencing `<src>_<suffix>_<func>_<window>`
2534
+ # (e.g. `PAYMENT_DATE_is_weekend_max_all_time`) survive parity.
2535
+ if agg and agg.windows and agg.agg_funcs:
2536
+ for src in dd.source_columns:
2537
+ for suffix in ("_delta_hours", "_hour", "_dow", "_is_weekend"):
2538
+ for window in agg.windows:
2539
+ for func in agg.agg_funcs:
2540
+ columns.add(f"{src}{suffix}_{func}_{window}")
2483
2541
  for text_cfg in event_cfg.text_features:
2484
2542
  if text_cfg.component_columns:
2485
2543
  columns |= set(text_cfg.component_columns)
File without changes
File without changes
File without changes
File without changes