All skills
dbt-labs avatar

/upgrading-dbt

@531557e official
by dbt Labsdbt-labs/dbt-agent-skills729 stars
62

Use when a user wants to upgrade, update, or migrate a dbt project to the latest version — e.g. "upgrade my dbt project," "migrate this off dbt 1.5," "get this project running on the latest dbt," "bump the dbt version." Upgrades a dbt v1 project (on 1.3, 1.4, 1.5, 1.6, or 1.7) all the way to 1.12, applying the required breaking, behavior, and deprecated changes from a data-driven issue corpus. Inputs — starting_version (the project's current dbt minor, one of 1.3/1.4/1.5/1.6/1.7) and adapter_type (snowflake/redshift/bigquery/databricks/spark); both are normally supplied by the caller (e.g. the dbt VS Code extension), with fallbacks described in the skill.

Use this Skill: https://skilld.dev/gh/dbt-labs/dbt-agent-skills/upgrading-dbt

This session only. Nothing lands on disk.

referencesexec-local.md

≈2.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Execution profile — local / VS Code extension

Mechanics for running upgrading-dbt where a shell is available. The rules, the phase order, and when to do each of these live in SKILL.md; this file only says how. If you are in dbt platform Studio, you want exec-platform.md instead — nothing here can run there.

Environment assumptions

  • The project directory is a git-versioned repo.
  • uvx and python are available — used to run scripts/tools.py, dbt-migrate-1x, and a throwaway dbt-core 1.12 for the parse gate.
  • scripts/tools.py sits under this skill's directory and owns all deterministic work.

Resolve $SKILL_DIR before your first command: the absolute path of the directory holding SKILL.md — where this skill was installed, which is not the project and not necessarily your shell's starting directory. Every command below is prefixed with:

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py"

Substitute the real path into each command literally. Each command runs in a fresh shell, so a variable you export in one does not exist in the next, and neither does a cd.

The script does not care where it is run from: it locates kb/ and references/ relative to its own file, and every command takes the project as --project-dir. An absolute script path is all it needs — so there is nothing here to discover by trial.

$PROJECT = the project's root directory. $ADAPTER = the adapter type (or none). $FROM = the starting version. <id> = an issue_id from the bundle.

State layout — two files, script-owned

Migration state lives in two separate files here:

File Holds Written by
target/dbt_migration_status.json Phase state — {version, updated_at, steps: [...]} status-init / status-set
target/dbt_migration_results.json Issue state — a map of issue_id → record init-results / set-status

scripts/tools.py is the only writer of both, and of migration_report.md. Never hand-write any of them, and never hand-edit a behavior flag — call the operation. The script validates every status value and phase id for you, and rewrites the status file atomically because a watcher polls it.

This is the one place the two profiles differ in kind rather than in syntax. The Studio profile has no shell, so it keeps the same state in a single file it writes directly. Do not carry that layout over here, and do not expect a migration started in one environment to resume in the other.

Operations

status-init

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" status-init --project-dir "$PROJECT"

status-set

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" status-set --project-dir "$PROJECT" \
  --step <phase-id> --status <status> \
  --note "12 of 41 issues checked"

--step is one of preflight, collect, read-project, detect, autofix, agentic-fixes, human-fixes, parse, re-detect, report. --status is pending / in_progress / waiting_input / complete / failed. Fails with exit 2 if status-init has not run yet.

preflight

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" preflight --project-dir "$PROJECT"

Prints JSON and exits non-zero when unsafe. Read ok; if false, relay reason.

load-bundle

The bundle is precompiled and shipped, so prefer simply reading it:

references/kb_<FROM>_<ADAPTER>.json      e.g. references/kb_1_5_snowflake.json

Versions are dotless (1.5 → 1_5) and the adapter is core when there is none. If that file is missing — a working copy where CI has not generated the bundles yet — build it from the local kb/ corpus:

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" collect --from-version "$FROM" --adapter "$ADAPTER"

That writes the same path and prints it. Then read it. Do not regenerate a bundle that already exists.

init-results

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" init-results \
  --from-version "$FROM" --adapter "$ADAPTER" --project-dir "$PROJECT"

Idempotent: preserves the status of any issue already recorded, which is what makes a run resumable.

set-status

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" set-status --project-dir "$PROJECT" \
  --issue-id <id> --status <status> \
  --files models/marts/customers.sql,models/staging/stg_orders.sql \
  --note "renamed + rewrote ref"

--files is comma-separated and repo-relative. --note and --files are optional. Exits 2 on an unknown status, an issue id not in the results artifact, or an attempt to move a resolved issue (fixed / applied / handled-by-autofix / flag-set) to skipped-not-present — see Step 8 in SKILL.md for why that transition is always a mistake.

list-issues

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" list-issues --project-dir "$PROJECT" \
  --status detected --automation-type agentic,deterministic --ids-only

--status and --automation-type both accept comma-separated values.

autofix

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" autofix --project-dir "$PROJECT" \
  --from-version "$FROM"

Runs dbt-migrate-1x --from "$FROM" --to 1.8 and returns the changed_files it touched, as JSON.

dbt-migrate-1x ships as its own standalone package — it only replays 1.x → 1.x version boundaries, so there is nothing here to confuse with dbt-autofix deprecations (the Fusion/v1.10 pass, which is not this migration). --from must be the project's real starting version rather than the tool's 1.3 default, so it replays exactly the hops the bundle covers; an out-of-range rule would change files that map onto no collected issue. --to stays at 1.8 because everything after it is behavior-flag gated and pinned by set-flag, never rewritten.

Local only. This shells out via uvx, which does not exist in Studio — see exec-platform.md's autofix operation for what to do there instead.

set-flag

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" set-flag --project-dir "$PROJECT" --issue-id <id>

Pins the flag named in that issue's behavior_flag.name to false under flags: in dbt_project.yml. Never hand-edit the file to do this.

parse

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" parse --project-dir "$PROJECT" \
  --adapter "$ADAPTER" --warn-error

Builds a throwaway dbt-core 1.12 virtualenv and a dummy profile as needed, runs dbt parse against it, and returns {ok, output}. The 1.12 venv is what makes this gate mean anything — never substitute the project's own dbt.

Pass the project's real $ADAPTER. It selects both the adapter installed in the venv and the type: of the synthesized profile, and those two have to agree: a mismatch fails the gate on profile resolution, before any project file is read, which reads like a project error and is not one. The credentials in that profile are fake by design — dbt parse never connects, so there is nothing here to ask the customer for, and a real profile would risk parse-time introspection reaching an actual warehouse.

jobs-file

$PROJECT/migration_jobs.json, already written by the extension before the handoff — it read the jobs from the dbt platform API, so the job ids, names and commands in it are authoritative. Read it with Read and record verdicts with Edit.

Read  $PROJECT/migration_jobs.json
Edit  $PROJECT/migration_jobs.json   # set status / updated / issue_id / reason per step

Never regenerate this file here, and never call the platform API for jobs. You have no credentials in this environment, and rewriting it would discard the ids the customer needs to find each job. If the file is absent, the project has no jobs or the extension could not read them — say so in the report and move on; do not invent one.

Schema and status vocabulary are in SKILL.md (Job commands). Every step must leave pending before Step 9.

revert

git -C "$PROJECT" restore <files>

Undoes uncommitted changes to those files only.

report

uv run --with pyyaml python "$SKILL_DIR/scripts/tools.py" report --project-dir "$PROJECT"

Renders target/dbt_migration_results.json → migration_report.md, grouped by outcome.

ask

Put the question to the user in chat and wait. Always status-set the current phase to waiting_input before asking, with a note saying what you asked.

No verify-commands here

SKILL.md's Step 7 runs checks in order: dbt parse first, then any further commands the profile names. This profile defines only the first check.

The reason is the profile it parses against. The parse operation builds a throwaway 1.12 venv with a synthesized profile holding fake credentials — which is exactly what makes it safe to run against an unfamiliar project, and exactly what makes it useless for anything that connects. dbt build against it does not fail informatively; it fails at connection, before reaching a single model. The project's own dbt and profile could connect, but that is the version being migrated away from, so a green build there proves nothing about 1.12.

So dbt parse is the end of verification here. Say that plainly in the report rather than implying more was proven than actually was: parse means the project parses on 1.12, not that its jobs still run. The commands in migration_jobs.json are still reviewed and given verdicts — they are just reviewed, not executed.

Source: SKILL.md on GitHub

1 warning8d3 checks · Risk MEDIUM
  • Gen Agent Trust Hub8d

    This skill automates the migration and upgrade of dbt projects. It requires access to sensitive local files such as project connection profiles and environment variables to function. It also executes various shell commands and manages Python environments to run migration scripts and verify project parsing. While the skill includes instructions to ignore potentially malicious content within user projects, users should be aware that it processes untrusted SQL and YAML files.

  • Socket8d

    No alerts

  • Snyk8d

    Risk: LOW · No issues

Signed by skilld at 531557e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated last week
What it can do
Runs commands Reads files Edits files
All 9 allowed tools
Bash(git:*)Bash(dbt:*)Bash(uvx:*)Bash(uv:*)ReadWriteEditGlobGrep
Other metadata
metadata
{
  "target_version": "1.12",
  "supported_source_versions": "1.3, 1.4, 1.5, 1.6, 1.7",
  "supported_adapters": "snowflake, redshift, bigquery, databricks, spark",
  "arguments": "starting_version={1.3|1.4|1.5|1.6|1.7}; adapter_type={snowflake|redshift|bigquery|databricks|spark}"
}

README badge

README badge for dbt-labs/dbt-agent-skills/upgrading-dbt