feat(dbt): add column-level meta to AGENTS.DBT_COLUMN - #36
Merged
Conversation
Column meta is where dbt projects record governance and semantic context
(PII flags, ownership, units, business labels). AGENTS.DBT_MODEL already
exposes model-level meta; agents reading DBT_COLUMN had no way to see the
same context at column granularity.
Serialize each column's meta dict to a JSON string, using the same
precedence as DBT_MODEL.meta: config.meta, then top-level meta, then {}.
dbt 1.10 and later nest column meta under config while earlier projects
declare it at the top level, so both are read.
Also list meta in the three built-in analyst skills' schema reference for
dbt_model and dbt_column, so agents know the field is queryable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
test_omni_run_upserts_root_before_source_tables called omni.run without omni being imported, so it errored with NameError on every run. Unrelated to the dbt change, but it was the only failure in the suite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verified against a real dbt 1.12 manifest that column meta and config.meta are always mirrored, so the spec's extra sentence about 1.10 nesting implied they were alternatives when they are not. Drop it and match the terse one-line style of the DBT_MODEL.meta row. Replace the dynamic meta-index lookup in the test with tuple unpacking, matching the positional style used in test_sigma and failing loudly if the row shape changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
smogili2
self-requested a review
July 31, 2026 22:49
smogili2
reviewed
Jul 31, 2026
| Column("column_name", "varchar", nullable=False), | ||
| Column("data_type", "varchar"), | ||
| Column("description", "text"), | ||
| Column("meta", "text"), |
Collaborator
There was a problem hiding this comment.
Since meta is always a JSON object, this can be marked as variant data type instead to make it easier for agents to know.
smogili2
reviewed
Jul 31, 2026
| self.calls.append(("upsert", table.name, list(rows))) | ||
|
|
||
|
|
||
| class DbtColumnMetaTests(unittest.TestCase): |
Collaborator
There was a problem hiding this comment.
Should also include dbt model meta column ingest test case.
abhijeethp
previously approved these changes
Aug 11, 2026
meta is always a JSON object (config.meta/meta, defaulting to {}), so a
TEXT column holding a serialized string forces every consumer to guess
that it's JSON before it can query into it. Add a json column kind,
distinct from the existing array kind (which is hard-typed as list<string>
on BigQuery/Databricks and would corrupt a dict), mapped to each
warehouse's native semi-structured type: Snowflake VARIANT (via the same
PARSE_JSON binding tags already uses), BigQuery JSON, Databricks VARIANT
(via parse_json).
DBT_MODEL.meta and DBT_COLUMN.meta both move together so the same field
doesn't disagree in type across sibling tables. _ingest now passes the
raw meta dict through instead of pre-serializing it with json.dumps,
mirroring how tags already flows as a raw list and letting each writer's
binding layer own the wire format.
Updates SPEC.md's DDL and source-field rows for both tables, and the
DBT_MODEL/DBT_COLUMN row fixtures in test_agents_schema_writer.py (which
predate this and encode meta as an already-serialized string). Restores
tests/test_dbt.py, which had been deleted from the working tree, with
assertions matching the new raw-dict convention.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Customer request: bring column
metainto thedbt_columnspec.Column-level
metais where dbt projects record governance and semantic context — PII flags, data owners, units, business-friendly labels, masking policies.AGENTS.DBT_MODELalready exposes model-levelmeta, so an agent can see that context for a table but not for the individual columns it is about to query.What changed
AGENTS.DBT_COLUMNgains onemeta TEXTcolumn holding the column's dbtmetadict serialized as a JSON string, mirroringAGENTS.DBT_MODEL.metaexactly.Precedence is the same rule
DBT_MODEL.metauses —column.config.meta, else top-levelcolumn.meta, else{}.src/agents_schema/dbt.py— new schema column plus one row value in_ingestSPEC.md— DDL, source-field table, and the summary-table descriptionmetaadded to thedbt_model/dbt_columnschema-reference rows, so agents know the field is queryable (thedbt_modelrow was already missing it)tests/test_dbt.py— new; covers meta fromconfig.meta, from top-levelmeta, and{}when absentWhy read both
config.metaand top-levelmetaVerified empirically rather than assumed, by parsing a project with dbt 1.12 that declares column meta both ways, and by diffing the published manifest JSON schemas:
ColumnInfo.configmetamirroredColumnInfo.metaOn current dbt the two locations are always populated identically, whichever YAML form the project author uses (
config: meta:, top-levelmeta:, ordbt_project.yml+meta:) — the same is true at model level. So on v12 either read alone would do. The top-level fallback is what keeps pre-1.10 manifests working, where columnconfigdoes not exist at all; readingconfigfirst is forward-compatible with dbt's deprecation of the legacy top-level field. Neither branch is dead across the supported range.Compatibility
Tables are
CREATE OR REPLACEd on every run, so the column appears on the next sync with no migration. Queries that name columns explicitly are unaffected;SELECT *consumers get one extra field. Type mapping verified across all three writers: SnowflakeTEXT, BigQuerySTRING, DatabricksSTRING.Scoped to
metaonly. Column-leveltagsalso exist in the manifest and would be a natural follow-up, but were left out of this change.Drive-by
tests/test_connector_root.pycalledomni.runwithout importingomni, so that test errored withNameErroron every run — the only failure in the suite. Fixed in a separate commit.Verification
Full suite:
End-to-end against a real dbt 1.12 manifest (not just the fixture), with
order_iddeclaring meta underconfig:andcustomer_emailat the top level:🤖 Generated with Claude Code