Skip to content

fix(ingestion): heal positional schema drift in class contracts - #1784

Merged
aleksdotbar merged 3 commits into
mainfrom
fix/class-contract-column-order
Jul 16, 2026
Merged

fix(ingestion): heal positional schema drift in class contracts#1784
aleksdotbar merged 3 commits into
mainfrom
fix/class-contract-column-order

Conversation

@aleksdotbar

@aleksdotbar aleksdotbar commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Problem

dbt-clickhouse incremental inserts map the model SELECT to table columns positionally, and union_by_tag unions members with SELECT *. Physical column order must equal model order. Three families diverged:

  • ai — label columns patched onto pre-existing tables via tail-appending ALTERs while the models declare them mid-SELECT. Syncs fail: CANNOT_PARSE_TEXT on staging inserts, NO_COMMON_TYPE on the class union.
  • collab chatdirect_and_group_messages added mid-SELECT with append_new_columns, no rebuild. Same drift latent on pre-existing member tables; the class table (default on_schema_change='ignore') never receives the column at all.
  • crm — salesforce members project custom_fields, hubspot members do not. Class union fails with NUMBER_OF_COLUMNS_DOESNT_MATCH whenever both connectors are configured.

Fix

  • ai labels leave silver. tool_label / surface_label are constants derived from tool / surface; the gold view now derives them via macros/ai_labels.sql (same pattern as git_source_label). Deletes the label backfill migration, both staging repair functions, and the mutations_sync=2 mutations they re-ran on every deploy.
  • Data columns get position pins. Idempotent ADD/MODIFY COLUMN ... AFTER heals in the migrate hook + 20260716000000_class_contract_heal.sql: conversation_count (ai), direct_and_group_messages (collab chat), custom_fields (crm). Guarded for absent tables and placeholders (the minimal gold-view placeholders lack the AFTER anchors).
  • crm parity. hubspot models emit a structural '{}' custom_fields at the salesforce position, in every positional branch.
  • Bounded gold builds. query_settings (memory, threads, disk spill) on both table-materialized observation models — deploy-time rebuilds degrade to disk spill instead of failing the release.

Every damaged instance converges on its next deploy + sync. No manual intervention, no knowledge of per-instance damage state needed: DROP preserves relative order, ADD/MODIFY AFTER are metadata-only no-ops once converged.

Verification

  • dbt parse clean; bash -n on both scripts.
  • Reviewed by CodeRabbit and Codex (high effort): one convergent finding (settings vs query_settings — table DDL settings would have rejected the memory bounds), fixed and re-verified against the adapter source. All heal anchors, types, and union branch parity verified against the model SELECTs.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added consistent human-readable labels for AI tools and usage surfaces across analytics.
    • Added custom_fields support to HubSpot CRM accounts, activities, contacts, deals, and users.
  • Data Quality

    • Standardized AI tool and surface identifiers with stricter validation.
    • Removed redundant label fields from AI usage data while preserving display labels.
  • Reliability

    • Improved schema migration and reconciliation for AI, CRM, and collaboration data.
    • Large analytics builds can now spill intermediate data to disk instead of failing due to memory limits.

dbt-clickhouse incremental inserts map the model SELECT to table
columns positionally, and union_by_tag unions members with SELECT *,
so physical column order must equal model order. Three families
diverged:

- ai: label columns were patched onto pre-existing tables via
  tail-appending ALTERs while the models declare them mid-SELECT;
  syncs fail (CANNOT_PARSE_TEXT on staging inserts, NO_COMMON_TYPE on
  the class union)
- collab chat: direct_and_group_messages was added mid-SELECT with
  append_new_columns and no rebuild; same drift latent on pre-existing
  member tables, and the class table never receives the column at all
- crm: salesforce members project custom_fields, hubspot members do
  not; the class union fails whenever both connectors are configured

Fixes:
- derive ai tool/surface labels in gold from the discriminator codes
  (macros/ai_labels.sql); drop the columns from the silver contract,
  the label backfill migration, the staging repair functions, and the
  per-deploy mutations they re-ran
- pin data columns (conversation_count, direct_and_group_messages,
  custom_fields) to their model position with idempotent
  ADD/MODIFY ... AFTER heals in the migrate hook, guarded for absent
  tables and placeholders
- add custom_fields as a structural '{}' to the hubspot models at the
  salesforce position; heal hubspot staging and crm class tables
- bound the table-materialized gold builds (query_settings: memory,
  threads, disk spill) so deploy-time rebuilds degrade to disk instead
  of failing the release

Every damaged instance converges on its next deploy + sync with no
manual intervention.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
@aleksdotbar
aleksdotbar requested a review from a team as a code owner July 15, 2026 19:56
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aleksdotbar, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a0971e9-7529-4c4f-8b3e-4dec0b30e7b1

📥 Commits

Reviewing files that changed from the base of the PR and between 6f714a2 and 0d22558.

📒 Files selected for processing (10)
  • src/ingestion/connectors/crm/hubspot/dbt/hubspot__crm_accounts.sql
  • src/ingestion/connectors/crm/hubspot/dbt/hubspot__crm_activities.sql
  • src/ingestion/connectors/crm/hubspot/dbt/hubspot__crm_contacts.sql
  • src/ingestion/connectors/crm/hubspot/dbt/hubspot__crm_deals.sql
  • src/ingestion/connectors/crm/hubspot/dbt/hubspot__crm_users.sql
  • src/ingestion/dbt/macros/ai_labels.sql
  • src/ingestion/gold/collab_metric_observations.sql
  • src/ingestion/gold/git_metric_observations.sql
  • src/ingestion/scripts/apply-ch-migrations.sh
  • src/ingestion/scripts/migrations/20260716000000_class_contract_heal.sql
📝 Walkthrough

Walkthrough

AI usage models now emit canonical discriminator codes and derive labels through shared macros. AI staging schemas remove denormalized labels, HubSpot models add structural custom_fields, deployment healing aligns physical contracts, and gold models receive explicit ClickHouse build settings.

Changes

AI contract and label normalization

Layer / File(s) Summary
Canonical AI discriminators and label macros
src/ingestion/connectors/ai/..., src/ingestion/dbt/macros/ai_labels.sql, src/ingestion/gold/ai_metric_observations.sql, src/ingestion/silver/ai/schema.yml
AI models emit standardized tool and surface codes, while gold dimensions derive display labels through shared macros and silver schemas validate accepted values.
AI physical schema alignment
src/ingestion/scripts/create-bronze-placeholders.sh, src/ingestion/scripts/migrations/20260716000000_class_contract_heal.sql, src/ingestion/scripts/apply-ch-migrations.sh
Placeholder schemas and existing AI staging tables remove label columns and normalize conversation_count placement and type.
CRM and collaboration contract alignment
src/ingestion/connectors/crm/hubspot/dbt/*, src/ingestion/scripts/apply-ch-migrations.sh
HubSpot union branches emit empty custom_fields, and deployment healing adds CRM and collaboration contract columns only to real tables.

Gold build settings

Layer / File(s) Summary
Bounded ClickHouse gold builds
src/ingestion/gold/collab_metric_observations.sql, src/ingestion/gold/git_metric_observations.sql, src/ingestion/scripts/apply-ch-migrations.sh
Gold models configure memory, thread, and external aggregation/sort thresholds, with comments describing spill-to-disk and materialization behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: cyberantonz, mitasovr

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: healing positional schema drift in class contract schemas.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/class-contract-column-order

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

aleksdotbar and others added 2 commits July 15, 2026 22:26
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
@aleksdotbar
aleksdotbar enabled auto-merge July 15, 2026 20:30
@aleksdotbar
aleksdotbar merged commit 75cc793 into main Jul 16, 2026
35 of 37 checks passed
@aleksdotbar aleksdotbar linked an issue Jul 17, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

class_ai_dev_usage, cursor__ai_dev_usage broken dbt

2 participants