Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/ingestion/connectors/ai/claude-team/connector.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,18 @@ streams:
- type: AddedFieldDefinition
path: [source_id]
value: "{{ config['insight_source_id'] }}"
# The month belongs in the key. This endpoint reports the billing
# month in progress and carries no history, so a month exists only in
# the rows read during it. Keyed on the seat alone, Bronze kept one
# row per seat — its latest state — and every closed month became
# unreproducible: a `dbt --full-refresh` (dispatched by a MAJOR
# descriptor bump) rebuilt Silver from that residue and silently
# dropped the history. Keyed on seat-and-month, Bronze retains the
# closing state of each month, which is the grain Silver already
# keeps, and a rebuild reproduces it.
- type: AddedFieldDefinition
path: [unique_key]
value: "{{ config['insight_tenant_id'] }}-{{ config['insight_source_id'] }}-{{ record['account_uuid'] }}"
value: "{{ config['insight_tenant_id'] }}-{{ config['insight_source_id'] }}-{{ record['account_uuid'] }}-{{ now_utc().strftime('%Y-%m') }}"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- type: AddedFieldDefinition
path: [collected_at]
value: "{{ now_utc().strftime('%Y-%m-%dT%H:%M:%SZ') }}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@
) }}

WITH latest_per_seat_month AS (
-- Bronze is full-refresh+append: every sync re-emits all seats with the
-- same unique_key (no date). Collapse to the latest snapshot per seat per
-- calendar month so each (seat, month) keeps its freshest spend reading.
-- Bronze is full-refresh+append and its unique_key carries the extraction
-- month, so it keeps each seat's closing state per month. Collapsing here
-- is still required: repeated syncs within a month re-emit the seat, and
-- rows written before the key carried a month have no month in theirs.
SELECT *
FROM {{ source('bronze_claude_team', 'claude_team_overage_spend') }}
WHERE account_uuid IS NOT NULL
Expand All @@ -60,9 +61,11 @@ WITH latest_per_seat_month AS (
SELECT
tenant_id AS insight_tenant_id,
source_id,
-- Silver dedup key: tenant-source-seat-month. The month component (absent
-- from the Bronze unique_key) preserves monthly history and gives intra-
-- month idempotency (latest snapshot wins via _version, same key).
-- Silver dedup key: tenant-source-seat-month, the same grain Bronze now
-- keys on. Derived here from the extraction timestamp rather than copied,
-- so rows written under the older key still land in the right month. The
-- month preserves history and gives intra-month idempotency (latest
-- snapshot wins via _version, same key).
CAST(concat(
coalesce(tenant_id, ''), '-',
coalesce(source_id, ''), '-',
Expand Down
9 changes: 8 additions & 1 deletion src/ingestion/connectors/ai/claude-team/descriptor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ name: claude-team
# conversation_count and declares tool_label — MAJOR per ADR-0015:
# pre-existing rows must re-materialize from Bronze to populate the new
# columns (one-shot scoped full refresh).
version: "2.0.0"
# 2.1.0: claude_team_overage_spend's unique_key gains the extraction month, so
# Bronze retains one row per seat per billing month instead of one per seat.
# MINOR per ADR-0015 — additive: existing rows keep their keys and stay valid,
# nothing has to re-materialize. Deliberately NOT major: a major bump
# dispatches a one-shot `dbt --full-refresh` (reconcile-connectors), which
# rebuilds Silver from Bronze — the very operation this change exists to make
# safe, and the one that cost the June history.
version: "2.1.0"
# Daily at 04:00 UTC — off-peak vs other connectors. The customer's
# proxy runs a single browser instance, so we avoid scheduling
# claude-team next to anything else that may also hit it.
Expand Down
Loading