Where dltHub's AI harness ends and Snowflake's CoCo begins

dltHub's AI harness writes the pipeline, Snowflake's CoCo takes over after data landed. Together they ✨

Share
Where dltHub's AI harness ends and Snowflake's CoCo begins

TL/DR: dltHub's commercial AI harness and Snowflake's Cortex Code (CoCo) sit on opposite sides of my pipelines. The former enables coding agents to build and maintain ingestion pipelines into a lakehouse... like ❄️ Snowflake. The latter is such a coding agent, specialized on working in said lakehouse. Together they ✨


The dynamic duo

dltHub's commercial offering includes a coding-agent harness: skills, rules, and MCP tools for Claude Code, Codex, Cursor, etc., running the full pipeline lifecycle through it (ingest, transform, explore, operate). Ten toolkits, forty skills. The whole thing ships as a .md file agents can pull straight into context:

πŸ‘‰ dlthub.com/ai-harness.md

Snowflake (among many other things πŸ˜…) sells CoCo. It is a coding agent too, but a specialized one: it understands a live Snowflake account and has deep knowledge around all things Snowflake, integrated into the agent out of the box. It executes SQL, reads schemas, profiles tables, and (per Snowflake's own benchmark) beats general-purpose coding agents specifically on data engineering tasks, because it doesn't first have to gather context from a README.

Neither vendor frames it this way, but placed side by side a difference becomes clear:

dltHub's harness owns everything around moving data and lives in files, CoCo owns everything that exists in the ❄️ account.

What actually happens when combined

About 6 months ago, I ran a few dozen dlt ingestion pipelines in Snowflake Container Services (SPCS): one Docker image and YAML spec per pipeline, external-access-integrations to allow ingress, and a compute pool to keep an eye on. All of it got torn down when dltHub launched. The only memory is the changelog entries in a Snowflake few tasks that used to call EXECUTE JOB SERVICE and now call TRIGGER_DLTHUB_PIPELINE() instead.

Ingestion (from Salesforce, our ERP, Jira, Confluence, SharePoint, Microsoft Graph, half a dozen ad platforms, Swiss federal statistics, and many more) now runs as dltHub Runtime jobs out of a single workspace. That migration alone carries most of the value prop: commercial dltHub swapped infrastructure work for authoring work plus integrated observability (soon including agentic monitoring and auto-healing 🀫). On a one-person data team, fewer moving parts is always better.

However, this setup could be done with any coding agent, really. Or without any if one prefers πŸ˜… The real magic trick comes next...

The part dltHub's harness doesn't reach

dltHub's harness equips a coding agent to write and deploy pipelines. But on top of this, it also enables getting tons of (meta) information out of the pipeline. Since dlt accesses the source system and "sees" the data in transition, the harness enables a coding agent to derive the structure of the source data, its taxonomy, build an ontology based on this and even scaffold a canonical data model.

And now hand all of this over to CoCo...

Equipped with the knowledge on what dltHub inserted into Snowflake, CoCo can do all kinds of really useful things:

Label the tables and their columns in Horizon catalog, β–Ύ
The cheapest win first: CoCo turns the harness's descriptions into comments and tags, so the catalog says what a column means and not only what type it has:
comment on column raw.salesforce.contact.npsp__do_not_contact__c
  is 'true if the person opted out of all contact; exclude from every campaign selection';

alter table raw.salesforce.contact
  modify column email set tag governance.tags.pii = 'email';
Once the tags are set, masking policies attached to them apply automatically. So does every agent that reads the catalog later, CoCo included.
build a data model on top using Dynamic Tables or dbt, β–Ύ
The harness's canonical data model is a scaffold, not a finished model. CoCo fills it in either as Dynamic Tables with a target_lag or as a dbt project running inside Snowflake or builds procedures from it. Either way it already knows the grain of each raw table and which child tables need to be joined back, because dlt let it know earlier.
design SPCS-hosted Streamlit apps visualizing that model, β–Ύ

Once the model exists, CoCo can scaffold a Streamlit app, containerize it and deploy it to SPCS. I still review these before anyone else sees them, but the step from "the model exists" to "someone can click on it" got a lot shorter.

πŸ‘‰ Retiring legacy BI with an agentic-built Streamlit app on SPCS

or - and this is my personal favorite:

build a Semantic View. β–Ύ
This is where all that "meaning" pays off. Cortex Analyst (and any agent on top of it) only answers as well as the semantic layer describes the data. A commented raw layer is good; a descriptive layer with relations, metrics and synonyms is better. CoCo can write that layer from what it now knows:
create or replace semantic view analytics.sv_donations
  tables (
    donors as raw.salesforce.contact primary key (id)
      with synonyms = ('supporters', 'donors')
      comment = 'people who gave at least once',
    gifts as raw.salesforce.opportunity primary key (id)
      comment = 'one row per closed donation'
  )
  relationships (
    gift_to_donor as gifts (npsp__primary_contact__c) references donors
  )
  facts (
    gifts.gift_amount as amount
  )
  dimensions (
    gifts.close_date as close_date,
    donors.postal_code as mailing_postal_code
  )
  metrics (
    gifts.total_donations as sum(gifts.gift_amount)
    comment = 'sum of closed donations in CHF'
  );

Semantic layers require an infusion of "meaning" for an analytical agent to understand the data well. A commented raw layer is good, a descriptive layer including relations and rules is better.

For example, take a column called "amount" on a Salesforce opportunity. It could be gross or net, pledged or paid, CHF or whatever currency the record was entered in. A human analyst might ask a colleague, but an agent picks the most plausible reading and writes valid SQL around it. Valid SQL built on a wrong assumption returns a wrong number with full confidence πŸ™ƒ A column comment removes some of the ambiguity.

However, comments stop at the object they're attached to. They can't say that gifts join to donors via whatever foreign key, or that the join is many-to-one. When an agent has to guess the join path, a classic mistake usually follows: a fan-out that counts every donation twice and raises no error anywhere. Declared relationships take that guess away.

Then there are rules that don't live in the data at all. Suppose "active donor" means a paid donation in the last 12 months. No column says that. Define it once as a metric, and every question about active donors runs through the same definition. Otherwise the agent reinvents it, slightly differently every time it's asked πŸ™„

Another effect would be easy to underestimate: Synonyms bridge the vocabulary gap... In a Swiss setup, questions come in German ("Wie viele Spenderinnen…?") while Salesforce columns are English API names with a ...__c suffix for custom fields. A semantic view narrows the search space. A Salesforce source alone can land dozens of tables with hundreds of custom columns, while the semantic view offers a curated handful of tables, dimensions and metrics. Fewer options mean fewer wrong picks, and fewer tokens spent exploring.

And finally, maybe the most important benefit of a semantic layer: When an answer is wrong, I can trace it to a definition and fix it once... instead of arguing with a prompt πŸ˜…

The boundary is gone

Would building all of this in Snowflake be possible with a different coding agent than CoCo? Of course!

Would it then happen behind Snowflake's security and accountability perimeter? Not so much.

And again, on the ADE-Bench (built by dbt Labs for realistic data-engineering tasks), CoCo scored 72.1% against 65.1% for both Claude Code and Codex, while burning 51% fewer tokens and requiring 8% less time than Claude Code (on Opus 4.7). A gap attributed to native Snowflake tool calls instead of falling back to bash or scratchpad scripts. Whether that margin holds outside a vendor benchmark is its own question, sure, but the design direction is legit: less shelling out, more direct API calls into the systems that matter.

And then, 🧊-berg

Just as an extra cherry on top: dltHub's commercial tier does more than deploy pipelines that write to Snowflake. It ships a Snowflake-specific destination "Snowflake+", that creates Apache Iceberg tables through Snowflake SQL and syncs them into Snowflake's Horizon (or Apache Polaris) catalog automatically.

[destination.snowflake]
external_volume = "my_external_volume"
iceberg_mode = "all"
catalog_sync = "my_open_catalog_int"

Small config, big consequence: ingestion layer and table format are no longer two separately maintained things bolted together, because both dltHub and CoCo are aware of Snowflake's own Iceberg catalog mechanics.

The combined value prop

dltHub's commercial harness made pipeline authoring an agent-first workflow, my own SPCS-to-dltHub migration is the receipt. Snowflake's CoCo is the only coding agent in with standing authority to say what's actually working best in Snowflake. Tied together, the two systems share tons of valuable knowledge about the data and the systems around it πŸ€“