feat(ai-overage): Claude Team per-seat overage → class_ai_overage → cc_overage bullet - #1371
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📥 CommitsReviewing files that changed from the base of the PR and between 7fc849e7e0eba768da9d00fa9b3dc5513d2aabad and 18f8eae. 📒 Files selected for processing (17)
✅ Files skipped from review due to trivial changes (5)
🚧 Files skipped from review as they are similar to previous changes (12)
📝 WalkthroughWalkthroughAdds end-to-end support for a new ChangesClaude Team cc_overage metric pipeline
Sequence Diagram(s)sequenceDiagram
participant Airbyte
participant Bronze as bronze_claude_team.claude_team_overage_spend
participant StagingModel as claude_team__ai_overage
participant Silver as silver.class_ai_overage
participant Gold as insight.ai_bullet_rows / ai_person_period
participant API as analytics-api metrics.query_ref
Airbyte->>Bronze: ingest overage_spend rows
Bronze->>StagingModel: dedup to latest snapshot per (account_uuid, month)
StagingModel->>StagingModel: compute overage_cents, is_over_limit, overage_metrics_json
StagingModel->>Silver: union_by_tag insert (tagged silver:class_ai_overage)
Silver->>Gold: ai_bullet_rows UNION branch emits metric_key=cc_overage
Gold->>Gold: ai_person_period classifies cc_overage as sum(metric_value)
Gold->>API: query_ref ARRAY JOIN includes (cc_overage, cc_overage_v)
API->>API: metric_catalog seeded with cc_overage thresholds
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
Paired FE PR: constructorfabric/insight-front#167 (renders the |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/components/connectors/ai/README.md (1)
201-213:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd language tags to fenced code blocks (Line 201 and Line 333).
These new fences are missing a language identifier and trigger
MD040.Proposed fix
-``` +```text silver/ ├── ai/ │ ├── class_ai_dev_usage (per-person-day code activity) │ ├── class_ai_assistant_usage (per-person-day chat/cowork/office/web) │ ├── class_ai_api_usage (per-key-or-project-day tokens + multi-modal API) │ ├── class_ai_cost (per-line-item-day financial — org/project total) │ ├── class_ai_overage (per-person-month spend-over-limit — BUILT: Claude Team) │ └── class_ai_audit_log (event-stream — admin actions, compliance) └── _shared/ ├── class_people (existing) — identity unification └── class_ai_directories? (optional — skills, connectors, projects metadata)```diff -``` +```text insight_tenant_id, source_id, unique_key, email, account_id period_month, tool, seat_tier, currency credit_limit_cents, used_amount_cents, overage_cents -- minor units (cents); overage = max(0, used − limit), honest-NULL if no limit is_over_limit, is_enabled, overage_metrics_json source, data_source, collected_at, _version</details> Also applies to: 333-339 <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@docs/components/connectors/ai/README.mdaround lines 201 - 213, Add the
language identifiertextto the two fenced code blocks that are missing
language tags. The first block contains the directory tree structure starting
with "silver/" and includes class definitions like class_ai_dev_usage and
class_ai_assistant_usage. The second block (also mentioned at line 333) contains
field names like insight_tenant_id, source_id, and period_month. Change both
opening fences from triple backticks with no language to triple backticks
followed bytextto resolve the MD040 linting error.</details> <!-- cr-comment:v1:d83b6511e9223367f898d08e --> _Source: Linters/SAST tools_ </blockquote></details> </blockquote></details>🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Inline comments: In `@src/backend/services/analytics-api/src/migration/m20260618_000002_seed_claude_team_overage_catalog.rs`: - Around line 175-177: The assert_eq! macro call within the seed_count_is_one() test function violates rustfmt formatting standards due to line length. Reformat the assert_eq! macro invocation to comply with Rust's standard formatting guidelines by breaking it into multiple lines with proper indentation, or run cargo fmt --all to automatically apply the correct formatting to the entire file. In `@src/ingestion/connectors/ai/claude-team/dbt/claude_team__ai_overage.sql`: - Around line 42-53: The deduplication in the latest_per_seat_month CTE uses only account_uuid and the month from _airbyte_extracted_at in the LIMIT 1 BY clause, but this does not match the model's full grain which also includes tenant_id and source_id. This causes records to be incorrectly dropped when the same account_uuid appears across different tenants or sources within the same month. Modify the LIMIT 1 BY clause to include tenant_id and source_id alongside account_uuid and toStartOfMonth(_airbyte_extracted_at) to ensure deduplication respects the complete model grain and prevents cross-source row loss. --- Outside diff comments: In `@docs/components/connectors/ai/README.md`: - Around line 201-213: Add the language identifier `text` to the two fenced code blocks that are missing language tags. The first block contains the directory tree structure starting with "silver/" and includes class definitions like class_ai_dev_usage and class_ai_assistant_usage. The second block (also mentioned at line 333) contains field names like insight_tenant_id, source_id, and period_month. Change both opening fences from triple backticks with no language to triple backticks followed by `text` to resolve the MD040 linting error.🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID:
cf155300-c004-4199-b724-3147300dad74📥 Commits
Reviewing files that changed from the base of the PR and between c32821d and 6bb2c83e74df9459571d50c6974d2b637687812c.
📒 Files selected for processing (16)
docs/components/connectors/ai/README.mddocs/components/connectors/ai/claude-team/specs/DESIGN.mddocs/components/connectors/ai/claude-team/specs/FEATURE.mddocs/components/connectors/ai/claude-team/specs/PRD.mdsrc/backend/services/analytics-api/src/migration/m20260610_000001_fix_ai_label_drift.rssrc/backend/services/analytics-api/src/migration/m20260618_000001_ai_claude_team_overage_metric.rssrc/backend/services/analytics-api/src/migration/m20260618_000002_seed_claude_team_overage_catalog.rssrc/backend/services/analytics-api/src/migration/mod.rssrc/ingestion/connectors/ai/claude-team/connector.yamlsrc/ingestion/connectors/ai/claude-team/dbt/claude_team__ai_overage.sqlsrc/ingestion/connectors/ai/claude-team/dbt/claude_team__bronze_promoted.sqlsrc/ingestion/connectors/ai/claude-team/dbt/schema.ymlsrc/ingestion/connectors/ai/claude-team/descriptor.yamlsrc/ingestion/scripts/migrations/20260618000000_ai-claude-team-overage-gold.sqlsrc/ingestion/silver/ai/class_ai_overage.sqlsrc/ingestion/silver/ai/schema.yml
|
Addressed CodeRabbit review in df12eeb5:
Re-validated: |
Introduce class_ai_overage — a unified per-person per-month AI
spend-vs-limit (overage) Silver class — and wire Claude Team as its
first contributor. Designed homogeneous so OpenAI/ChatGPT seats can
join later by emitting the same 19-column positional contract under
tag silver:class_ai_overage (vendor extras → overage_metrics_json,
never new columns).
Claude Team (now unblocked: the proxy sessionKey has Owner role, so
/overage_spend_limits returns 200 with all 149 per-seat rows):
- bronze claude_team_overage_spend gains scalar fields currency /
out_of_credits / used_credits_basis / limit_type (confirmed live)
- promote claude_team_overage_spend to RMT (ADR-0002)
- claude_team__ai_overage.sql: bronze → Silver contributor. Grain =
latest snapshot per (seat, billing month); unique_key carries the
month so a new month never overwrites a prior month's closing value.
UNITS: used_credits / monthly_credit_limit are already cents (USD) —
mapped straight to *_cents with NO ×100 (unlike cost_cents in
claude_team__ai_dev_usage). overage_cents = max(0, used - limit),
honest-NULL when no limit is known.
- class_ai_overage.sql: union_by_tag('silver:class_ai_overage')
- schema.yml docs/tests for both layers
- descriptor 1.2.0 → 1.3.0 (additive; populates only with billing:view/Owner)
Validated: dbt parse + ls green; tag:claude-team+ now builds the
overage chain; tag:silver:class_ai_overage resolves to the contributor.
Will populate on the next claude-team sync (Bronze table created with
0 rows when the session lacked billing:view).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
Address non-blocker review notes on the class_ai_overage introduction: - docs: add class_ai_overage to the AI coverage matrix (docs/components/connectors/ai/README.md) — coverage-by-class row, silver-layer split tree, and a dedicated subsection next to class_ai_cost explaining the distinction (cost = org/project total financial; overage = per-person spend above their own monthly limit), the 19-col contract, units, month grain, and per-provider feeding (Claude Team BUILT; OpenAI/Windsurf future). Notes Claude Team is the first live source despite predating the matrix's column set. - tests: currency gains accepted_values ['USD'] on both staging and silver (extend when a non-USD billing tenant appears). - tests: used_amount_cents gains not_null on silver (always populated; mirrors staging). email stays nullable on silver by design — future OpenAI seats attribute via account_id without email (same precedent as class_ai_dev_usage silver email). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
Take Claude Team overage from Silver up to the metric API so the frontend can render it. Adds metric_key `cc_overage` (per-seat spend above the monthly credit limit, in cents). - Gold: 20260618000000_ai-claude-team-overage-gold.sql — DROP+CREATE insight.ai_bullet_rows with a new Branch 6 reading silver.class_ai_overage (source='claude_team'): emits cc_overage = overage_cents, dated at toDate(collected_at) so the current month's snapshot lands inside the dashboard's rolling window (period_month's 1st would miss short windows). Branches 1–5 unchanged. honest-NULL: only rows with a computable overage (overage_cents NOT NULL) are emitted; within-limit seats emit a real 0. - analytics-api m20260618_000001: extend Team/IC Bullet AI query_ref with cc_overage_v — countIf-guarded sumIf (honest-NULL like prs), NOT in ACTIVE_LIST → averaged per person over the period like cc_cost. 22 → 23 metric_keys. Shape tests added. - analytics-api m20260618_000002: seed metric_catalog + product-default threshold for cc_overage — label "Claude Overage", unit ¢, higher_is_better=false, good ≤ 0¢ / warn ≤ 5000¢. Tests added. - Register both migrations in migration/mod.rs. Validated: cargo test migration → 85 passed, 0 failed. FE (cyber-insight-front bullet-layout-groups) is the paired change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
Review gap: cc_overage was added to the query_ref rollup (Path A) but not to the parallel ai_person_period period-rollup (Path B), so it silently defaulted to avg() — the exact issue #1286 defect class. For a monthly overage snapshot, avg divides by each person's active-day count (understated), while a per-period spend counter must sum. - Gold migration 20260618000000 now also DROP+CREATEs insight.ai_person_period with cc_overage added to the sum branch (twin of cc_cost). Counters→sum, active→max, ratios→avg preserved. - Guard test m20260610_000001: add cc_overage to SUM_KEYS and to the emitted-key list BULLET_ROWS_KEYS (branch 6) so every_bullet_key_is_classified_not_defaulting_to_avg actually covers it; add a focused cc_overage_sums_like_cc_cost spot-check. Documented that the emitted-key list is hand-maintained (false-green risk for any future gold branch key not added here). cargo test migration → 86 passed, 0 failed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
The PRD/DESIGN/FEATURE described claude-team as Bronze-only (`dbt_select: ''`, "No Silver, no Gold") but Silver has since landed: `claude_team__ai_dev_usage` → class_ai_dev_usage (INSIGHT-458) and now `claude_team__ai_overage` → class_ai_overage (Gold bullet `cc_overage`, descriptor 1.3.0). Update DESIGN §4.4, PRD §3.2 non-goals, and FEATURE §1 to state Silver landed and point to the cross-connector contract. Verified end-to-end locally on kind-insight with real overage data (149 seats from the dev proxy, Owner role): bronze 149 → silver 149 (formula invariant 0 violations, 33 dbt tests) → gold 148 cc_overage (honest-NULL) → ai_person_period sum-classified (proven 314 vs avg 157) → analytics-api migrations applied to MariaDB (probes green) → stored query_ref run against CH returns cc_overage=2.317¢, 23 keys, no regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
- claude_team__ai_overage: LIMIT 1 BY now includes tenant_id + source_id (was account_uuid + month only) so a multi-tenant / multi-instance bronze_claude_team doesn't drop same-account_uuid rows across tenants /sources as false duplicates — matches the model's full grain. No behaviour change for single-tenant (local e2e: 149 → 149). - docs README: add `text` language to two bare code fences (MD040). - cargo fmt the two touched migration files. Re-validated: cargo test migration 86 passed; fmt --check clean; dbt re-run on local CH still 149 silver rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
…d view The ai_bullet_rows cc_overage branch (20260618000000_ai-claude-team-overage-gold.sql) does CREATE VIEW ... FROM silver.class_ai_overage. CH 24.x validates a view's source tables at CREATE time, so in any env where migrations run before dbt builds the silver model (E2E, fresh deploy) the view creation fails with Code 60 UNKNOWN_TABLE and aborts the whole migration step. Mirrors the existing class_ai_dev_usage / class_support_activity placeholders: an empty ReplacingMergeTree(_version) table tagged INSIGHT_PLACEHOLDER_v1 (dropped and rebuilt by dbt via drop_silver_placeholders_at_start). Columns match the claude_team__ai_overage staging output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
Backtick the bare identifiers (claude_team, ComingSoon, cc_overage, cc_cost, silver.class_ai_overage) in the m20260618_* overage migration doc comments and the m20260610_000001 entries touched by this PR. `cargo clippy --all-targets` runs with -D warnings, so these tripped doc_markdown and failed Lint & Test. Doc-comment-only change (clippy --fix); 97 migration tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
7fc849e to
e104962
Compare
What
Introduces AI overage end-to-end for Anthropic / Claude Team: a new unified Silver class
class_ai_overage(per-person-per-month spend over the monthly credit limit) surfaced all the way to a Gold AI bulletcc_overage.Designed homogeneous so OpenAI/ChatGPT can join later by emitting the same contract (deferred by PM for now).
Layers
class_ai_overage(19-col positional contract) + first contributorclaude_team__ai_overagefrom the existingclaude_team_overage_spendstream (now unblocked: the proxy sessionKey has Owner/billing:view, so/overage_spend_limitsreturns all 149 seats).currency/out_of_credits/used_credits_basis/limit_type; promoted to RMT.used_credits/monthly_credit_limitare already cents (USD) → mapped straight to*_cents, no ×100 (unlikecost_cents).overage_cents = max(0, used − limit), honest-NULL when no limit.unique_keypreserves history.ai_bullet_rowsBranch 6 →cc_overage; classifiedsumin both rollups (query_refPath A andai_person_periodPath B — the AI bullet metrics: ai_person_period rolls up Codex/cc counters as daily-avg (and PRs as fake zeros) — wrong period aggregation #1286 defect class).query_refextended (22→23 keys, honest-NULL guarded, averaged per person likecc_cost) +metric_catalogseed (label "Claude Overage", unit ¢, lower-is-better, good ≤ 0¢ / warn ≤ 5000¢). Guard test updated to cover the new key.class_ai_overageadded to the AI coverage matrix; claude-team PRD/DESIGN/FEATURE reconciled (were Bronze-only); descriptor 1.2.0 → 1.3.0.Validation
cargo test migration→ 86 passed, 0 failed.cc_overage(honest-NULL) →ai_person_periodsum-classification functionally proven (314 vs avg-bug 157) → analytics-apimigrateon MariaDB clean (probes green) → exact storedquery_refvs ClickHouse returnscc_overage = 2.317¢, 23 keys, no regression.Paired FE
Renders the
cc_overagebullet (catalog-driven): constructorfabric/insight-frontfeat/ai-overage-frontend.🤖 Generated with Claude Code
Summary by CodeRabbit
cc_overagemetric (USD, cents)cc_overageto enable it in analytics viewscc_overageaggregates as a summing counter (not average)cc_overageis mapped end-to-end and classified correctly