# Add a Customer Lifetime Value Model to a dbt Project from a Linear Issue

Yes. An agent picks up the Linear issue, adds a customer lifetime value mart to your dbt project with YAML tests and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/add-customer-ltv-model/dbt/), in a Project that has your repository.

## What the agent does

The run starts from the repository URL in `resources`: the agent clones it into its own cloud sandbox and reads the project the way a new analytics engineer would. In a project like `dbt-labs/jaffle-shop`, `dbt_project.yml` names the project `jaffle_shop` at version 3.0.0, requires dbt `>=2.0.0`, materializes staging as views and marts as tables, and points tests at `data-tests/`. `packages.yml` already pulls `dbt-labs/dbt_utils` at 1.4.1.

The agent maps the issue onto the existing marts before writing SQL. `models/marts/customers.sql` already joins `stg_customers` to an orders summary and exposes `count_lifetime_orders`, `first_ordered_at`, `last_ordered_at` and `lifetime_spend`, with matching column docs and tests in `customers.yml`. The issue still asks for a dedicated LTV mart with lifetime revenue, order count and first and last order dates, documented in YAML with tests, so the agent adds `models/marts/customer_ltv.sql` that selects those fields into a focused one-row-per-customer table rather than expanding the broader customers overview again.

The new model refs `orders` the same way the customers summary does, groups by `customer_id`, and names the columns the issue uses in reviews: lifetime revenue from `order_total`, order count, and the min and max of `ordered_at`. Alongside it the agent adds `models/marts/customer_ltv.yml` with a model description, `not_null` and `unique` on `customer_id`, and `dbt_utils.expression_is_true` style checks that lifetime revenue is non-negative when orders exist.

Proving it means a local DuckDB build. The agent installs dbt against the project's Python tooling from `Taskfile.yml` and `requirements.txt`, configures a DuckDB profile, runs `dbt deps` for `dbt_utils`, seeds or uses the sample data the project already ships under `seeds/`, and runs `dbt build` so the new mart and its YAML tests compile and pass before the pull request opens.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "A Linear issue labeled agent says: \"Add a Customer Lifetime Value Model to a dbt Project. Add a mart model with each customer's lifetime revenue, order count and first and last order dates, documented in YAML with tests.\" Do the work in this repository, add tests for it, run dbt build on DuckDB, and open a pull request that links back to the issue.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **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.
2. **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.
3. **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. Its diff adds `models/marts/customer_ltv.sql` and `models/marts/customer_ltv.yml`, and leaves the existing `customers` mart in place. The description quotes the issue and links back to it.

The checks on the pull request run `dbt build` on DuckDB with the same package versions `packages.yml` pins. A reviewer reads the SQL for one grain (one row per customer), confirms the four metrics match the issue, and confirms the YAML tests fail if `customer_id` duplicates or lifetime revenue goes negative. Staging models and seeds are untouched.

The session read keeps the record behind the pull request: `dbt deps`, the DuckDB profile the agent wrote, and the full `dbt build` output. Anyone who questions the mart can re-read those commands without re-running them locally.

## Run it on every Linear issue labelled agent

The Project's automation turns the label into the run. In the console, add an automation with a Linear trigger on `linear.issue.labeled` filtered to the `agent` label and this prompt; each labelled analytics ticket starts its own run in its own sandbox and opens its own pull request on the dbt project.

If the automation is not wanted, a small server can do the same: receive Linear's issue webhook, put the mart definition from the issue into `input`, and POST to `/o/{org}/sessions` with the jaffle-shop style repository in `resources`. A `run.completed` webhook on the API key posts the pull request URL when `dbt build` has finished in the sandbox.

## Why run it on Rush?

On a laptop this job is a chain of Python and warehouse setup: a venv, `pip install` from `requirements.txt`, a DuckDB or warehouse profile, and `dbt deps` before `dbt build` will even resolve `dbt_utils`. Each run on Rush boots its own sandbox with that already in reach, runs the build itself, and keeps every command and its output, so the proof of the LTV mart is the run's own record.

Analytics backlogs stack up when several labelled issues wait on one engineer's laptop. Each labelled issue becomes one run, one sandbox and one pull request, so LTV, retention and revenue marts can move in parallel without fighting over a single dbt target directory.

## Frequently asked questions

### customers.sql already has lifetime spend. Why add another model?

The customers mart is a broad overview with customer type and tax breakdowns. The issue asks for a dedicated LTV mart with lifetime revenue, order count and first and last order dates. The agent adds `customer_ltv` for that contract and leaves `customers` alone so existing dashboards keep their columns.

### Does it run against Snowflake or BigQuery?

The prompt asks for DuckDB in the sandbox so the build is self-contained. The SQL uses standard dbt refs and aggregations that port to the warehouse your profile already uses; change the profile when you merge if production is not DuckDB.

### Which package provides the YAML tests?

`packages.yml` already depends on `dbt-labs/dbt_utils` 1.4.1. The agent runs `dbt deps` and uses those helpers for expression tests instead of inventing a second testing package.

### Will the agent regenerate seed data?

Only if the existing seeds are missing in the sandbox. The Taskfile's `gen` and `seed` tasks exist for jafgen output; a normal LTV change reuses `seeds/` and does not rewrite years of fake orders.
