Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Pilot a Databricks Hive Metastore (HMS) → Unity Catalog (UC) migration before the deadline — audit every table's migratability, produce a dependency-ordered migration plan, trace UC's two-level permission model, and pick an environment isolation pattern. Use when a user asks how to migrate off the Hive metastore, move to Unity Catalog, which tables are UC-ready, why a table won't migrate, why a user lacks access after migration, or how to isolate dev/test/prod under UC. Trigger with "migrate to
.claude/skills/jeremylongshore-databricks-uc-migration-pilot/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 101% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 170% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 139% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 397% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 41% | 0% |
Pilots a Hive Metastore → Unity Catalog migration end to end: it audits which tables can migrate (and why the rest cannot), turns that audit into a dependency-ordered execution plan, traces UC's two-level permission model when access breaks, and picks an environment-isolation pattern for the one-metastore-per-region constraint.
The September 30, 2026 forcing function. Databricks is deprecating the legacy Hive Metastore. On the cutover date the hive_metastore catalog goes read-only — no new writes, no schema changes — and stays available only for read-through while you migrate. Everything that writes to HMS (jobs, DLT pipelines, dashboards, external BI) must be pointed at a Unity Catalog table before then, or it breaks. This is not optional and it is not fast: the blockers below (legacy storage schemes, DBFS-root managed data, DENY-based ACLs) each require a physical data move or a grant rewrite, so the work is measured in weeks, not an afternoon.
> ⚠️ Verify the exact date and scope against your workspace's current > deprecation notice (Databricks account console → Previews / Announcements). > Deprecation dates move; this skill's job is to get you ready well ahead of > whatever the live date is — treat 2026-09-30 as the planning deadline, not an > excuse to wait for it.
This skill does four things, in order, and each is deterministic where it can be:
scripts/audit-hms-readiness.py classifies every HMS table by itsstorage URI into READY (cloud-native, UC-governable), BLOCKED (a named un-migratable condition), or ORPHAN (a dangling HMS row pointing at deleted storage). The LLM never eyeballs a URI; the script owns the verdict.
migration-planner subagent turns the readiness CSV into aper-table plan (SYNC / DEEP CLONE / rewrite / skip) ordered by dependencies so a view never migrates before its base tables.
uc-permission-tracer subagent walks UC's two-level accessmodel to answer "why can't user X read this?" in one pass — replacing ~90 minutes of doc-spelunking per ticket.
/uc-env-pattern-picker recommends one of four dev/test/prodisolation patterns under the single-metastore-per-region limit.
It is architecturally distinct from the v1 databricks-upgrade-migration and databricks-migration-deep-dive skills: those narrate migration in prose. This one runs a real readiness audit, emits a dependency-ordered plan, and traces live permissions. Deep, load-on-demand knowledge lives in references/; the arithmetic-and-classification lives in scripts/.
The three role grants below are the hard dependencies and the most common reason the pilot stalls mid-flow. The skill checks the role chain first (Step 1) and reports exactly what is missing before doing any work.
(system.information_schema, system.access) the audit reads, and to create the metastore/assign it to workspaces. Enable at the account level, not per workspace.
CREATE CATALOG, run GRANT on UC objects,and create external locations + storage credentials. Migration is a sequence of metastore-admin operations.
s3/IAM, Azure, or GCP) — required to diagnose astorage-credential failure (the D2 case: a table is cloud-native but the UC storage credential's IAM role cannot assume the bucket). Bash(aws:*) is allowed for read-only IAM inspection; the skill never mutates cloud IAM.
databricks auth login, or theDATABRICKS_HOST + DATABRICKS_TOKEN env pair) and jq for parsing. Enumeration and grant SQL run through the CLI Statement Execution API.
DATABRICKS_WAREHOUSE_ID set to a running SQL warehouse — everystatement-execution call requires it.
Authentication. All auth comes from the environment: the CLI's DATABRICKS_HOST + DATABRICKS_TOKEN (or databricks auth login), and read-only cloud credentials from the standard provider chain (aws sts get-caller-identity to confirm). No secrets are hardcoded.
The pipeline is verify role chain → detect readiness → plan → migrate → trace access → isolate envs. Do them in order; Step 1 is a hard gate.
Before touching data, confirm the running principal holds the roles the migration needs. A missing role surfaces here, not three tables into a SYNC.
bash# Am I authenticated, and as whom? databricks current-user me | jq -r '.userName' # Metastore-admin check: only a metastore admin can read every grant. If this # errors with PERMISSION_DENIED, the principal is not metastore-admin. databricks api post /api/2.0/sql/statements --json "$(jq -n --arg wh "$DATABRICKS_WAREHOUSE_ID" \ '{warehouse_id:$wh, wait_timeout:"30s", statement:"SELECT 1 FROM system.information_schema.catalog_privileges LIMIT 1"}')" \ | jq -r '.status.state, (.status.error.message // "ok")'
Report the role chain status plainly: account-admin (needed to enable system schemas), metastore-admin (needed for grants + external locations), cloud IAM read (aws sts get-caller-identity). If any is missing, name who must grant it and STOP — do not start an audit you cannot finish.
The readiness audit reads system.information_schema; permission tracing reads system.access. Those schemas are individually gated — enabling one does not enable the others — behind an account-level flag AND a metastore-admin grant chain. If they are not enabled, run the bundled idempotent enabler (account-admin auth; a workspace PAT is rejected up front) to enable each schema and grant a group USE CATALOG system + USE SCHEMA + SELECT in one pass:
bashpython3 "${CLAUDE_SKILL_DIR}/scripts/enable-system-schemas.py" \ --account-id "$DATABRICKS_ACCOUNT_ID" --metastore-id "$METASTORE_ID" \ --grant-to data-governance --dry-run # drop --dry-run to apply
Then confirm a UC metastore is attached to this workspace (databricks metastores current) — without one there is nowhere to migrate to. The full two-layer access model (account-admin enables, metastore-admin grants, neither inherits SELECT) is in ${CLAUDE_SKILL_DIR}/references/system-tables-access-model.md.
Enumerate the HMS schema(s) and classify every table. The script owns the verdict; feed it either live (--live SCHEMA) or the enumerated rows as JSON.
bashOUT="${OUT:-$(pwd)/uc-migration-out}" && mkdir -p "$OUT" # Live enumeration (needs DATABRICKS_WAREHOUSE_ID + an authenticated CLI): python3 "${CLAUDE_SKILL_DIR}/scripts/audit-hms-readiness.py" \ --live sales --summary --out "$OUT/readiness-sales.csv"
Every row lands in one of three buckets — READY (cloud-native path, SYNC-able), BLOCKED (a named condition), ORPHAN (dangling HMS entry, cleanup not migration). When a BLOCKED class appears, load ${CLAUDE_SKILL_DIR}/references/uc-migration-blockers.md for that class's canonical error, the physical relocation procedure, and the per-cloud variant. Do not skip the CLONE-drops-history gotcha in that file — a shallow CREATE TABLE ... CLONE breaks time travel post-migration; use DEEP CLONE.
Hand the readiness CSV to the migration-planner subagent (see agents/migration-planner.md) with the org's catalog naming convention. It emits a numbered execution order with per-step rationale and the right verb per table (SYNC for ready Delta externals, DEEP CLONE/rewrite for blocked, CREATE VIEW after base tables, skip for orphans). Dependency ordering is the point: a view never migrates before the tables it reads.
Invoke it via /audit-hms-readiness (which runs Step 3 then routes here) or directly by handing the subagent the CSV path.
Walk the plan top to bottom:
FROM hive_metastore.<schema>.<table> — metadata-only, no data copy. SYNC SCHEMA does a whole schema when every table is ready.
DEEP CLONEfor Delta, CREATE TABLE AS SELECT for non-Delta) to a UC-governed cloud path, register an external location + storage credential, then create the UC table at the new path. Full procedure per class in uc-migration-blockers.md.
auto-map to UC's allow-only model — re-author them (Step 6 traces them).
Re-run Step 3 after a batch to confirm the migrated tables drop out of BLOCKED.
When a user hits PERMISSION_DENIED or AccessDenied on s3://… post-migration, route to the uc-permission-tracer subagent (/trace-uc-permission <user> <error>). It walks UC's two levels — account-admin/metastore-admin status → group membership (including nested-group caveats) → catalog/schema/table grants → the external-location/storage-credential grant for a cloud AccessDenied — and returns a single actionable line: "user X needs group Y membership AND `GRANT SELECT ON <obj>` run by metastore admin W."
When the fix is "add the user to group Y" but the grant still does not apply, the cause is usually the Entra→Databricks SCIM bridge silently dropping nested-group membership — see ${CLAUDE_SKILL_DIR}/references/scim-bridge-patterns.md for the connector's direct-members-only limitation and the three workarounds.
For a fresh UC layout, run /uc-env-pattern-picker. It asks compliance, cost, and BI-tool questions and recommends one of four patterns for the one-UC-metastore-per-region constraint, emitting a matching Databricks Asset Bundle target stub. The four patterns, their cost models, and tradeoffs live in ${CLAUDE_SKILL_DIR}/references/uc-environment-isolation-patterns.md.
$OUT/readiness-<schema>.csv, in the working dir) — one rowper HMS table: table_name, storage_uri, scheme, migration_blocker, suggested_action, each table bucketed READY / BLOCKED / ORPHAN with a READY/BLOCKED/ORPHAN tally.
migration-planner) — anumbered execution order, the verb per table (SYNC/DEEP CLONE/rewrite/skip), per-step rationale, and views ordered after their base tables.
uc-permission-tracer) — the exact missing groupmembership + grant + who must run it, for a specific user + error.
and a ready-to-paste DAB target stub.
with the exact grant and grantor for anything missing.
| Error | Cause | Solution | |-------|-------|----------| | PERMISSION_DENIED on system.information_schema | Principal is not metastore-admin, or system schemas not enabled | Step 1/2: report the missing role; an account admin enables system schemas, a metastore admin grants access. Stop until resolved. | | No metastore attached to the workspace | UC not set up for this workspace | databricks metastores current; if empty, an account admin must create/assign a metastore before any migration. | | SYNC fails with an unsupported-location error | Table is on a legacy scheme (wasbs:///adl:///dbfs:/) | It is BLOCKED, not READY — relocate per uc-migration-blockers.md (DEEP CLONE to a cloud path), then create the UC table. | | Time travel broken after migration | Shallow CREATE TABLE ... CLONE was used | Use DEEP CLONE — shallow clone copies metadata only and drops Delta history. Re-clone with DEEP CLONE. See the gotcha in uc-migration-blockers.md. | | AccessDenied on s3://… after a READY table migrates | UC storage credential's IAM role cannot assume the bucket | Trace with /trace-uc-permission; verify the external location's storage credential role and the bucket trust policy with read-only aws calls (D2). | | DESCRIBE DETAIL returns no location | Orphaned HMS entry (deleted storage) or a view | The audit marks it ORPHAN — clean up or skip; never report it as a migration blocker. | | DATABRICKS_WAREHOUSE_ID unset | No SQL warehouse for statement execution | Set it to a running warehouse before Step 1. |
Runs the full pipeline. Step 1 confirms metastore-admin, Step 3 audits the schema (12 READY, 5 BLOCKED, 2 ORPHAN), the planner emits a numbered order (SYNC the 12 ready Delta tables first, DEEP CLONE the 3 dbfs:/user/hive managed tables to s3://, re-CREATE the 2 Parquet externals, skip the 2 orphans, then the 4 views), and the skill walks the plan — surfacing the Sept-30-2026 deadline and the CLONE-history gotcha inline.
Step 3 only. The audit CSV lists each BLOCKED table with its scheme and the one-line reason; the skill loads uc-migration-blockers.md for each class and gives the physical relocation procedure per cloud.
Routes straight to /trace-uc-permission alice@corp.com "PERMISSION_DENIED: SELECT on main.sales.orders". The tracer returns: "alice@corp.com is in no group with a grant; add her to `data-analysts` AND run `GRANT SELECT ON TABLE main.sales.orders TO data-analysts` as metastore admin — she is not account-admin, so the grant will not inherit."
/uc-env-pattern-picker asks the compliance/cost/BI questions and, for a cost-sensitive team that accepts shared lineage, recommends single-metastore-catalog-per-env (bronze_dev/bronze_prod) with a DAB var.env-parameterized target stub — noting the lineage-cleanliness tradeoff and the multi-account alternative for hard isolation.
${CLAUDE_SKILL_DIR}/references/uc-migration-blockers.md — the taxonomy of un-migratable HMS conditions: canonical error, physical relocation procedure, per-cloud variant, and the CLONE-drops-history gotcha.${CLAUDE_SKILL_DIR}/references/uc-environment-isolation-patterns.md — the four dev/test/prod isolation patterns under one-metastore-per-region, with cost models and a DAB target stub.${CLAUDE_SKILL_DIR}/references/system-tables-access-model.md — the two-layer access model (account-admin enables, metastore-admin grants), per-schema enablement, and the manual permission traversal.${CLAUDE_SKILL_DIR}/references/scim-bridge-patterns.md — the Entra→Databricks SCIM nested-group limitation and the three workarounds.${CLAUDE_SKILL_DIR}/scripts/audit-hms-readiness.py — deterministic HMS-table readiness classifier (READY/BLOCKED/ORPHAN → CSV).${CLAUDE_SKILL_DIR}/scripts/enable-system-schemas.py — idempotent system-schema enabler + group grant chain (account-admin auth precheck).${CLAUDE_SKILL_DIR}/agents/migration-planner.md — turns the readiness CSV into a dependency-ordered plan.${CLAUDE_SKILL_DIR}/agents/uc-permission-tracer.md — traces UC's two-level access model for a user + error.SYNC command · UCX (Databricks Labs)| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 17,357 | 34,852 | +101% | 1 | 1 | 0% | 3,219 | 4,923 | +53% | 0 | 0 | — |
case-02 | pass→fail | 10,889 | 6,823 | -37% | 1 | 1 | 0% | 2,201 | 4,813 | +119% | 0 | 0 | — |
case-03 | pass→pass | 17,930 | 17,742 | -1% | 1 | 1 | 0% | 3,288 | 7,567 | +130% | 0 | 0 | — |
case-04 | fail→pass | 15,914 | 7,532 | -53% | 1 | 1 | 0% | 2,859 | 5,733 | +101% | 0 | 0 | — |
case-05 | fail→pass | 11,375 | 6,866 | -40% | 1 | 1 | 0% | 2,119 | 5,712 | +170% | 0 | 0 | — |
case-06 | pass→pass | 8,521 | 5,767 | -32% | 1 | 1 | 0% | 1,542 | 5,524 | +258% | 0 | 0 | — |
case-07 | pass→pass | 11,439 | 8,517 | -26% | 1 | 1 | 0% | 1,977 | 6,003 | +204% | 0 | 0 | — |
case-08 | fail→pass | 15,623 | 11,893 | -24% | 1 | 1 | 0% | 2,802 | 6,698 | +139% | 0 | 0 | — |
case-09 | pass→pass | 5,492 | 2,484 | -55% | 1 | 1 | 0% | 1,065 | 4,879 | +358% | 0 | 0 | — |
case-10 | pass→pass | 14,553 | 12,400 | -15% | 1 | 1 | 0% | 2,387 | 6,480 | +171% | 0 | 0 | — |
case-11 | pass→pass | 8,307 | 8,395 | +1% | 1 | 1 | 0% | 1,284 | 5,766 | +349% | 0 | 0 | — |
case-12 | pass→pass | 8,213 | 9,969 | +21% | 1 | 1 | 0% | 1,475 | 6,387 | +333% | 0 | 0 | — |
case-13 | pass→pass | 10,632 | 4,572 | -57% | 1 | 1 | 0% | 1,777 | 5,175 | +191% | 0 | 0 | — |
case-14 | pass→pass | 13,304 | 13,137 | -1% | 1 | 1 | 0% | 2,283 | 6,670 | +192% | 0 | 0 | — |
case-15 | pass→pass | 11,769 | 12,616 | +7% | 1 | 1 | 0% | 2,080 | 6,816 | +228% | 0 | 0 | — |
case-16 | fail→pass | 6,018 | 4,340 | -28% | 1 | 1 | 0% | 1,062 | 5,273 | +397% | 0 | 0 | — |
case-17 | pass→pass | 10,391 | 11,987 | +15% | 1 | 1 | 0% | 1,902 | 6,533 | +243% | 0 | 0 | — |
case-18 | pass→pass | 6,770 | 5,225 | -23% | 1 | 1 | 0% | 1,265 | 5,337 | +322% | 0 | 0 | — |
case-19 | fail→pass | 18,366 | 4,436 | -76% | 1 | 1 | 0% | 3,748 | 5,301 | +41% | 0 | 0 | — |
case-20 | fail→pass | 7,434 | 2,743 | -63% | 1 | 1 | 0% | 1,333 | 4,875 | +266% | 0 | 0 | — |
case-21 | pass→pass | 16,146 | 14,656 | -9% | 1 | 1 | 0% | 3,128 | 7,056 | +126% | 0 | 0 | — |
case-22 | pass→pass | 12,922 | 10,337 | -20% | 1 | 1 | 0% | 2,852 | 6,632 | +133% | 0 | 0 | — |
case-23 | pass→pass | 11,993 | 10,292 | -14% | 1 | 1 | 0% | 2,646 | 6,686 | +153% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 21 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +22 percentage points is the difference between those two pass rates over the 21 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.