Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Run the Data Contract CLI (`datacontract test`) against ODCS contracts in the project to verify the live data still conforms — schema, quality rules, and freshness. Handles two kinds of contracts with different semantics: output-port contracts under `models/output_ports/**/*.odcs.yaml` (tested against this project's warehouse — "am I still producing what I promised?") and input-port contracts under `models/input_ports/*.odcs.yaml` (tested against the upstream warehouse — "is upstream still produ
.claude/skills/hashgraph-online-datacontract-test/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 144% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 137% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 131% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 161% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 114% | 0% |
Run the Data Contract CLI (datacontract test) against contracts in the project to check whether the data currently produced by a warehouse still matches the schema and quality rules declared in the contract.
Two kinds of contracts live in this project and they test against different warehouses:
models/output_ports/v<N>/*.odcs.yaml — what this data product commits to produce. They test against this project's warehouse. A failure means we are no longer producing what we promised.models/input_ports/*.odcs.yaml — cached snapshots of what we trust upstream to produce. They test against the upstream provider's warehouse, using a server block from upstream's ODCS. A failure means upstream drifted from the contract we trusted; the consequence is that our output may break too. Treat input-port failures as an upstream incident, not a local bug.datacontract-edit (it edits, tests, and classifies the failure as breaking-or-not).--logs.> ${PLUGIN_ROOT} below refers to the root of this plugin — the directory that contains skills/. On Claude Code it is set automatically as ${CLAUDE_PLUGIN_ROOT} — use that. On any other agent (Codex, Copilot CLI, etc.) it is unset; resolve it as ../.. relative to this SKILL.md file's directory (i.e. the grandparent of skills/<this-skill>/).
Before running Step 0, print this plan to the user verbatim:
> Running datacontract-test. I'll: > 1. Pre-checks: confirm the datacontract CLI is on PATH and the server credentials are available. > 2. Pick which contract(s) to test — defaults to all models/output_ports/**/*.odcs.yaml and models/input_ports/*.odcs.yaml. > 3. Pick the server (defaults to production if the contract has one). > 4. Run datacontract test per contract and capture the result. > 5. Report pass/fail with per-rule detail; flag missing credentials separately from real failures.
Then proceed.
uv run --quiet datacontract --version succeeds from the project root. If it fails, run uv sync (the bootstrap template seeds datacontract-cli[all] as a dev dep in pyproject.toml) and retry. If uv sync still doesn't make it available, stop and tell the user to verify datacontract-cli[all] is listed in pyproject.toml's [dependency-groups].dev. Do not propose uv tool install here — per-project venv is the convention.*.odcs.yaml exists under models/output_ports/**/ or models/input_ports/. If not, stop and tell the user there's nothing to test.servers block and list the env vars the chosen server type needs (e.g. DATACONTRACT_SNOWFLAKE_USERNAME / ..._PASSWORD, DATACONTRACT_DATABRICKS_TOKEN, DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON). If any are unset, surface the list to the user and ask whether to continue (the CLI will fail-fast on that server) or stop. Do not try to source credentials yourself.models/output_ports/**/*.odcs.yaml and models/input_ports/*.odcs.yaml.CONTRACTS. For each entry, also remember its role (output or input) — Step 4 surfaces failures differently.For each contract in CONTRACTS:
production. If production isn't defined, ask the user which one.--server all if the user explicitly asks to test every server.For each contract:
uv run datacontract test <path-to-contract>.odcs.yaml --server <server> --logsWhere <path-to-contract> is the file resolved in Step 1 — typically models/output_ports/v<N>/<file>.odcs.yaml for output contracts, or models/input_ports/<file>.odcs.yaml for input contracts. The CLI does not care which directory; the role only matters for how Step 4 reports the result.
--logs ensures per-rule failure detail is in stdout — without it the CLI only prints a summary.--output ./test-results/<contract>.xml --output-format junit.--publish $API/test-results where $API is the Entropy Data host. Don't publish by default — it writes server-side state.Run sequentially, not in parallel — the warehouse is the bottleneck and parallel runs muddy the log output.
End with this two-part recap. Use the shared Status enum (created, updated, already present, deferred, skipped); for this skill the relevant statuses are passed, failed, and skipped (missing creds).
Part 1 — outcome table. One row per contract tested. Group the rows: output-port contracts first, then input-port contracts under a sub-header (so the reader sees the two roles at a glance).
| Contract | Role | Server | Result | Failures | Details | |---|---|---|---|---|---| | <contract-file> | output / input | <server> | passed / failed / skipped | count or — | one line per failing rule (field + rule), or "missing env var: …" if skipped |
Part 2 — next steps. Bullet list, include only what applies. Treat output vs. input failures differently:
orders.order_id: not_null violated for 17 rows). The fix is in this project — either the dbt model is wrong, the contract is wrong, or the data is wrong. If the user wants a follow-up SQL to find the offending rows, suggest the shape but don't run it. If failures look like they came from a contract edit (rules tightening), point at datacontract-edit to classify breaking-vs-additive.dataproduct-implement once upstream republishes a corrected contract, so the cached snapshot under models/input_ports/ refreshes.skipped row, the exact env vars the user needs to set, and where to get them (usually the warehouse admin or entropy-data connection get).If everything passed, write a single line: All <N> contracts pass against <server>.
The Data Contract CLI reads credentials from environment variables, not from the contract file. Only the connection topology (host, database, schema, etc.) belongs in the servers block. The examples below cover the most common warehouses. Other types (Oracle, MySQL, Trino, DuckDB, Kafka, ...) follow the same pattern; see the Data Contract CLI README for the full list.
ODCS server block:
yamlservers: production: type: snowflake account: abcdefg-xn12345 database: ORDER_DB schema: ORDERS_PII_V2
Any env var prefixed DATACONTRACT_SNOWFLAKE_ is forwarded to the Snowflake connector with the prefix stripped and the rest lowercased, so you can pass any Snowflake/Soda parameter this way. Three auth modes:
Password auth
bashexport DATACONTRACT_SNOWFLAKE_USERNAME=... export DATACONTRACT_SNOWFLAKE_PASSWORD=... export DATACONTRACT_SNOWFLAKE_WAREHOUSE=COMPUTE_WH export DATACONTRACT_SNOWFLAKE_ROLE=DATA_CONTRACT_TEST
Private key (JWT) auth — used for service accounts and CI:
bashexport DATACONTRACT_SNOWFLAKE_USERNAME=SVC_DATACONTRACT export DATACONTRACT_SNOWFLAKE_AUTHENTICATOR=SNOWFLAKE_JWT export DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PATH=/secrets/snowflake_rsa.p8 # Only if the key is encrypted: export DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PASSPHRASE=... export DATACONTRACT_SNOWFLAKE_WAREHOUSE=COMPUTE_WH export DATACONTRACT_SNOWFLAKE_ROLE=DATA_CONTRACT_TEST
External browser SSO — interactive, for local runs against an IdP-backed account:
bashexport DATACONTRACT_SNOWFLAKE_USERNAME=jane.doe@example.com export DATACONTRACT_SNOWFLAKE_AUTHENTICATOR=externalbrowser export DATACONTRACT_SNOWFLAKE_WAREHOUSE=COMPUTE_WH export DATACONTRACT_SNOWFLAKE_ROLE=DATA_CONTRACT_TEST
Not usable in CI — it opens a browser window.
ODCS server block:
yamlservers: production: type: databricks host: adb-1234567890.7.azuredatabricks.net # optional, can also come from env catalog: acme_catalog_prod schema: orders_latest
The datacontract CLI does not share auth state with the databricks CLI — a token must be supplied explicitly via DATACONTRACT_DATABRICKS_TOKEN. When surfacing missing credentials to the user, recommend the OAuth-first path; fall back to PAT only when OAuth isn't available.
Recommended — short-lived OAuth from the already-authenticated databricks CLI:
bashexport DATACONTRACT_DATABRICKS_TOKEN=$(databricks auth token | jq -r .access_token) export DATACONTRACT_DATABRICKS_HTTP_PATH=/sql/1.0/warehouses/<warehouse-id>
Token is valid ~1h, the literal value never lands in shell history, and a leaked token expires before most attackers notice — much smaller blast radius than a long-lived PAT.
Fallback — Personal Access Token (use when databricks auth token isn't available: PAT-only profile, OAuth refresh issue, headless shell):
bashexport DATACONTRACT_DATABRICKS_TOKEN=dapi... export DATACONTRACT_DATABRICKS_HTTP_PATH=/sql/1.0/warehouses/<warehouse-id>
A PAT is long-lived until rotated. Scope it narrowly (read access to the data product's schema is enough) and avoid putting the export in .bashrc/.zshrc — it persists in shell history.
CI — use a service-principal-issued token (M2M OAuth, or an SP-owned PAT), not a personal one, with SELECT scoped to the data product's schema. Set as a repository secret named DATACONTRACT_DATABRICKS_TOKEN.
Optional env vars:
bashexport DATACONTRACT_DATABRICKS_SERVER_HOSTNAME=adb-... # only needed if `host` is not in the server block
ODCS server block:
yamlservers: production: type: postgres host: db.example.internal port: 5432 database: analytics schema: public
Env vars:
bashexport DATACONTRACT_POSTGRES_USERNAME=datacontract_ro export DATACONTRACT_POSTGRES_PASSWORD=...
Both are required. Use a read-only role.
ODCS server block:
yamlservers: production: type: athena catalog: awsdatacatalog # optional, default is awsdatacatalog schema: orders_db regionName: eu-central-1 stagingDir: s3://acme-athena-results/datacontract/
Env vars:
bashexport DATACONTRACT_S3_ACCESS_KEY_ID=AKIA... # required export DATACONTRACT_S3_SECRET_ACCESS_KEY=... # required export DATACONTRACT_S3_REGION=eu-central-1 # optional, overrides regionName export DATACONTRACT_S3_SESSION_TOKEN=... # optional, for STS temporary creds
The IAM principal needs athena:* on the workgroup, glue:Get* on the catalog, and read/write on the stagingDir bucket prefix.
ODCS server block:
yamlservers: production: type: bigquery project: acme-data-prod dataset: orders
Two auth modes:
Service account key file
bashexport DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH=/secrets/bq-sa.json
Application Default Credentials (ADC) — no env vars needed. Used automatically when DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH is unset. Works with gcloud auth application-default login for local runs and with Workload Identity Federation in CI.
Optional impersonation:
bashexport DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT=datacontract@acme-data-prod.iam.gserviceaccount.com
The principal needs bigquery.dataViewer on the dataset and bigquery.jobUser on the project.
Fabric Warehouse and Lakehouse SQL endpoints speak the SQL Server wire protocol, so use type: sqlserver.
ODCS server block:
yamlservers: production: type: sqlserver host: abc123def.datawarehouse.fabric.microsoft.com port: 1433 database: orders_wh schema: dbo driver: ODBC Driver 18 for SQL Server
Fabric only accepts Entra ID (Azure AD) auth, not SQL logins. Pick one of:
Service principal — for CI:
bashexport DATACONTRACT_SQLSERVER_AUTHENTICATION=ActiveDirectoryServicePrincipal export DATACONTRACT_SQLSERVER_CLIENT_ID=<app-registration-client-id> export DATACONTRACT_SQLSERVER_CLIENT_SECRET=<client-secret>
User password — Entra ID username + password (no MFA):
bashexport DATACONTRACT_SQLSERVER_AUTHENTICATION=ActiveDirectoryPassword export DATACONTRACT_SQLSERVER_USERNAME=jane.doe@acme.com export DATACONTRACT_SQLSERVER_PASSWORD=...
Interactive — opens a browser, for local dev only:
bashexport DATACONTRACT_SQLSERVER_AUTHENTICATION=ActiveDirectoryInteractive export DATACONTRACT_SQLSERVER_USERNAME=jane.doe@acme.com
The same env vars work for a regular on-prem SQL Server; switch DATACONTRACT_SQLSERVER_AUTHENTICATION=sql and supply DATACONTRACT_SQLSERVER_USERNAME / DATACONTRACT_SQLSERVER_PASSWORD.
Install ODBC Driver 18 locally (brew install msodbcsql18 on macOS, apt-get install msodbcsql18 on Debian/Ubuntu) before running.
datacontract test which executes SELECT queries; it never writes. Do not invoke datacontract publish, datacontract export, or entropy-data datacontracts put from this skill..env, ~/.aws, or anywhere else on the user's behalf.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-05 | fail→fail | 7,457 | 21,150 | +184% | 1 | 1 | 0% | 1,181 | 5,185 | +339% | 0 | 0 | — |
case-01 | fail→fail | 14,847 | 10,981 | -26% | 1 | 1 | 0% | 269 | 5,125 | +1805% | 0 | 0 | — |
case-02 | fail→fail | 7,930 | 9,686 | +22% | 1 | 1 | 0% | 378 | 4,939 | +1207% | 0 | 0 | — |
case-03 | fail→fail | 2,592 | 11,370 | +339% | 1 | 1 | 0% | 296 | 4,928 | +1565% | 0 | 0 | — |
case-04 | fail→fail | 15,494 | 9,879 | -36% | 1 | 1 | 0% | 1,679 | 5,034 | +200% | 0 | 0 | — |
case-06 | fail→pass | 10,916 | 20,733 | +90% | 1 | 1 | 0% | 2,259 | 5,513 | +144% | 0 | 0 | — |
case-07 | fail→pass | 12,981 | 27,748 | +114% | 1 | 1 | 0% | 2,545 | 6,025 | +137% | 0 | 0 | — |
case-08 | fail→pass | 20,067 | 6,639 | -67% | 1 | 1 | 0% | 2,196 | 5,079 | +131% | 0 | 0 | — |
case-09 | fail→pass | 13,680 | 11,855 | -13% | 1 | 1 | 0% | 2,029 | 5,296 | +161% | 0 | 0 | — |
case-10 | fail→pass | 23,895 | 9,372 | -61% | 1 | 1 | 0% | 2,596 | 5,553 | +114% | 0 | 0 | — |
case-11 | pass→pass | 13,745 | 11,977 | -13% | 1 | 1 | 0% | 1,506 | 5,476 | +264% | 0 | 0 | — |
case-12 | fail→pass | 16,860 | 6,219 | -63% | 1 | 1 | 0% | 2,024 | 5,010 | +148% | 0 | 0 | — |
case-13 | pass→pass | 11,146 | 7,279 | -35% | 1 | 1 | 0% | 1,454 | 4,504 | +210% | 0 | 0 | — |
case-14 | fail→fail | 22,801 | 10,518 | -54% | 1 | 1 | 0% | 2,677 | 4,849 | +81% | 0 | 0 | — |
case-15 | pass→pass | 15,405 | 12,412 | -19% | 1 | 1 | 0% | 1,724 | 5,398 | +213% | 0 | 0 | — |
case-16 | pass→pass | 16,515 | 23,053 | +40% | 1 | 1 | 0% | 2,467 | 5,475 | +122% | 0 | 0 | — |
case-17 | fail→pass | 11,890 | 6,255 | -47% | 1 | 1 | 0% | 1,806 | 5,243 | +190% | 0 | 0 | — |
case-18 | fail→pass | 14,975 | 4,906 | -67% | 1 | 1 | 0% | 2,197 | 4,767 | +117% | 0 | 0 | — |
case-19 | fail→pass | 19,959 | 8,869 | -56% | 1 | 1 | 0% | 2,065 | 4,693 | +127% | 0 | 0 | — |
case-20 | fail→fail | 17,965 | 19,087 | +6% | 1 | 1 | 0% | 2,254 | 4,911 | +118% | 0 | 0 | — |
case-21 | fail→pass | 6,136 | 19,024 | +210% | 1 | 1 | 0% | 1,024 | 6,712 | +555% | 0 | 0 | — |
case-22 | fail→pass | 13,617 | 23,176 | +70% | 1 | 1 | 0% | 1,623 | 5,242 | +223% | 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. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 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 +50 percentage points is the difference between those two pass rates over the 21 comparable cases. 2 cases got worse with the skill loaded, and they are 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.