What the agent does
The agent clones the dbt project into its own cloud sandbox and takes stock of what is tested before adding anything. In dbt-labs/jaffle-shop, dbt_project.yml names the profile default, keeps tests in data-tests/, builds staging models as views and marts as tables, and only enables the seeds in seeds/jaffle-data/ when the variable load_source_data is true. Each model has a YAML file beside its SQL. Staging is mostly covered: every stg_ model has unique and not_null on its key, stg_order_items.order_id has a relationships test to stg_orders, and stg_orders has a dbt_utils.expression_is_true check. The marts are uneven: customers, orders and order_items have tests, while locations, products and supplies have none, and the six sources in __sources.yml have none either.
Then it reads the SQL and the seed data to decide which test fits each column, rather than putting unique on every column that ends in _id. A primary key gets unique and not_null; a foreign key such as orders.location_id or supplies.product_id gets relationships to the model that owns it; a column whose values are a closed set gets accepted_values, with the set taken from the data (raw_products.csv has a type column, and customers.customer_type already lists new and returning). Grain matters too: stg_supplies says it has one row per supply cost, not per supply, so supply_id repeats and must not get unique; its key is supply_uuid, which is already tested.
It writes the tests in the syntax the project already uses, data_tests: with an arguments: block for relationships and accepted_values, so the new lines read like the existing ones. To run them it installs dbt with the DuckDB adapter, checks that the version satisfies the project's require-dbt-version: ">=2.0.0", writes a default profile that points at a DuckDB file in the sandbox rather than in the repository, runs dbt deps for dbt_utils and dbt-audit-helper from packages.yml, and runs dbt build --vars '{"load_source_data": true}', which loads the seeds, builds the models and runs every test in dependency order.
When a new test fails on the seed data, the agent does not delete it or loosen it until it passes. It queries the failing rows in DuckDB, records the count and a few examples, keeps the test, and writes the failure up in the pull request: either the data breaks a rule the model promises, or the test was the wrong rule for that column, and the person who knows the data decides which.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Add dbt data tests to every model and source that lacks them. First list every model, its grain and its existing tests, and read each model's SQL and the seed data before choosing a test. Use unique and not_null on primary keys, relationships on foreign keys to the model that owns them, and accepted_values where a column is an enum, with the values taken from the data. Do not put unique on a column the model's description says repeats. Follow the YAML style the project already uses. Install dbt with the DuckDB adapter at a version that satisfies require-dbt-version, write a profile with the project's profile name pointing at a DuckDB file outside the repository, run dbt deps, then run dbt build with the vars that load the seeds. If a new test fails, keep it, query the failing rows and report the count and examples in the pull request instead of removing or weakening it. Open a pull request.",
"resources": [
{
"url": "https://github.com/your-org/your-app"
}
]
}- Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
- Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
- Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.
What comes back
What comes back is a pull request on its own branch that only edits YAML. For a project like jaffle-shop, the diff adds data_tests to models/marts/locations.yml, products.yml and supplies.yml, fills the gaps in the other mart and staging files, and adds tests to the sources in models/staging/__sources.yml. The description has a table of every test added, by model and column, and a separate section for any test that fails on the seed data, with the failing row count and sample rows from DuckDB.
The proof is the dbt build output in the session transcript: the seeds loading, each model building, and each test's pass or fail, from a run in the sandbox. jaffle-shop's own ci.yml starts dbt Cloud jobs on Snowflake, BigQuery and Postgres with the project's dbt Cloud credentials, so on a copy without those secrets the transcript is where the tests are shown running; in your repository, whatever dbt CI you have runs on the pull request.
Before merging, a reviewer reads the failing-test section first and decides, test by test, whether the data or the rule is wrong, then checks that every relationships test points at the model that owns the key.
Run it on every night
This job is meant to run on a schedule, since new models land during the day and their tests often do not. A cron job or scheduled workflow on your side sends POST /o/{org}/sessions each night with the repository in resources, and the agent adds tests only to what is still untested, so a night with nothing new ends with no pull request.
Give the API key a webhook_url and Rush posts run.completed or run.failed to it when the run's turn ends, so the morning starts with the pull request link or the reason there is none.
The use case this job follows, with its automation and trigger: Write the missing tests.
Why run it on Rush?
Choosing the right test needs the data, not only the YAML: an accepted_values list is only right if it was read from the column, and a unique test is only safe if the grain allows it. In a Rush sandbox the agent installs dbt and DuckDB, loads the seeds and runs dbt build on a machine that exists for this run, so it can query the actual rows behind every choice without credentials to your warehouse.
The transcript keeps every dbt command and its output, including the queries the agent ran against failing tests, so the reviewer can see why each test exists and what it found. A test that fails is reported with its evidence, never quietly removed to make the run green.
Frequently asked questions
Does it need access to our Snowflake or BigQuery warehouse?
No. It builds the project on DuckDB in the sandbox from the repository's seeds. If your project has no seeds, say in input what sample data to use, or give it a read-only warehouse connection; the tests it writes are adapter-neutral YAML either way.
What happens when a new test fails?
The test stays in the pull request. The agent queries the failing rows, puts the count and a few examples in the description, and leaves the decision to you: fix the model, fix the data, or drop that one test.
Will it add unique tests to columns that legitimately repeat?
The prompt tells it to read each model's grain first. In jaffle-shop, stg_supplies has one row per supply cost, so supply_id repeats and gets no unique test, while supply_uuid is the key.
Does it use dbt_utils or dbt-expectations tests?
It uses the four built-in generic tests by default and the packages already in packages.yml where they fit. It does not add a new package unless you ask for one in input.