Skip to content

feat(claude-team): keep a seat's overage reading per day, not per month - #2714

Merged
Gregory91G merged 3 commits into
mainfrom
feat-overage-daily-readings
Aug 21, 2026
Merged

feat(claude-team): keep a seat's overage reading per day, not per month#2714
Gregory91G merged 3 commits into
mainfrom
feat-overage-daily-readings

Conversation

@Gregory91G

@Gregory91G Gregory91G commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Refs #2709.

Why. The seat endpoint reports a running month-to-date total and keeps no history, so one API object is one reading of a seat. Bronze keyed a reading by seat and extraction month over a ReplacingMergeTree, so every read inside a month was the same row and the newest replaced the rest: a month's closing figure survived, the trajectory that produced it did not. #2528 made this exact move from seat-only to seat-and-month, and stopped one level short.

What changed. The bronze key carries the read day, the record carries it as snapshot_date, and a new class_ai_overage_daily carries the readings into silver.

class_ai_overage is untouched. It holds a month's closing state, a data-quality check joins on it, and the invoice-to-seat price join needs one row per seat per month. The daily relation sits beside it rather than replacing it, and takes only the columns a trajectory needs — it is not a copy of the monthly model.

MINOR, deliberately not MAJOR. Existing rows keep their keys and nothing re-materializes. A major bump dispatches the one-shot dbt --full-refresh that this change exists to give something to rebuild from — the same reasoning recorded for 2.1.0.

A mirror this missed. snapshot_date reached the connector schema and the bronze DDL but not src/ingestion/tests/e2e/metrics/schemas/bronze_claude_team.claude_team_overage_spend.yaml, the hand-maintained fixture mirror that carries additionalProperties: false. The gap stays latent until a fixture first sets the field, which is why this PR was green and #2715 was the one that failed on it. Fixed there.

Deploy (void)

Do not run the sequence below. Its only window was the first build of claude_team__ai_overage_daily, which has already happened, so there is nothing left for it to preserve. Kept because the model's INVARIANT: comments refer to the reasoning.

A ReplacingMergeTree collapses on merge, not on insert, so readings already replaced logically may still sit in unmerged parts. The first build of claude_team__ai_overage_daily is the one chance to keep them: it reads bronze without FINAL and dedups per day rather than per bronze key, then never rebuilds. That rescues a tail of recent days, never a history, and how much depends only on what has not merged by then.

SYSTEM STOP MERGES bronze_claude_team.claude_team_overage_spend;
-- run dbt so claude_team__ai_overage_daily is built
SYSTEM START MERGES bronze_claude_team.claude_team_overage_spend;

Both properties are recorded as INVARIANT: in the model — a later reader would reasonably take the missing FINAL for an oversight.

Out of scope. Nothing reads the new relation yet. The metric that distributes the billed cost across days is #2709's other half and needs the e2e cases the metric-coverage gate requires, so it follows separately.

Verified. dbt parse clean. audit_rmt_read_dedup.py reports no new gap for either model. Connector, descriptor and silver schema parse as YAML. Not run: dbt cannot execute here, so the models are unbuilt and the window functions in the follow-up are unexercised — this PR adds none.

The seat endpoint reports a running month-to-date total and no history, so one
API object is one reading of a seat. Bronze keyed a reading by seat and
extraction month over a ReplacingMergeTree, so every read inside a month was the
same row and the newest replaced the rest: a month's closing figure survived and
the trajectory that produced it did not. #2528 made the same move from seat-only
to seat-and-month, for the same reason, and stopped one level short.

Key a reading by the day it was taken and carry that day in the record as
snapshot_date, matching the two ChatGPT Team snapshot streams — the only other
streams in the repository whose key comes from the clock rather than the payload,
and both already keyed by day. A re-run on the same day still replaces its own
row rather than adding a second.

`class_ai_overage_daily` carries the readings into silver beside the existing
class_ai_overage, which is untouched: it holds a month's closing state, a
data-quality check joins on it and the invoice-to-seat price join needs one row
per seat per month.

MINOR per ADR-0015, for the reason 2.1.0 was: existing rows keep their keys,
nothing re-materializes, and a major bump would dispatch the one-shot
`dbt --full-refresh` that this change exists to give something to rebuild from.

Refs #2709

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>
@Gregory91G
Gregory91G requested a review from a team as a code owner August 20, 2026 17:06
@Gregory91G Gregory91G added component:ingestion dbt / approved connector scope team:data Ingestion and dbt labels Aug 20, 2026
@Gregory91G Gregory91G self-assigned this Aug 20, 2026
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 92d81270-a0bf-490a-8183-2d999ed21515


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The committed snapshot under scripts/connectors-ddl is what a fresh warehouse is
built from, and CI re-dumps it to fail on drift. class_ai_overage_daily creates a
silver table, so the snapshot gains its DDL — taken from the dump the gate
produced rather than reconstructed by hand.

Refs #2709

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>
…readings

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>

# Conflicts:
#	src/ingestion/connectors/ai/claude-team/descriptor.yaml
@Gregory91G
Gregory91G enabled auto-merge August 21, 2026 07:49
@Gregory91G
Gregory91G added this pull request to the merge queue Aug 21, 2026
Merged via the queue into main with commit f865428 Aug 21, 2026
31 checks passed
@Gregory91G
Gregory91G deleted the feat-overage-daily-readings branch August 21, 2026 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:ingestion dbt / approved connector scope team:data Ingestion and dbt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants