Skip to content

fix: replace fan-out attribution with single-owner additive rollup and "Unassigned" bucket for dimension rankings - #5168

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
07-14-fix_unassigned_category_for_dashboard_charts
Jul 17, 2026
Merged

Pratham-Mishra04 merged 1 commit into
devfrom
07-14-fix_unassigned_category_for_dashboard_charts

Conversation

@impoiler

@impoiler impoiler commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the multi-value fan-out attribution model for dimension rankings (Teams, Business Units, Customers, Users, Virtual Keys) with a single-owner additive model. Previously, a request shared across multiple teams was credited to every team it touched, causing double-counting and making per-team spend non-additive relative to the org total. Now each request is attributed to exactly one owner (the scalar ID column), and requests with no scalar owner collapse into a synthetic "Unassigned" bucket instead of being dropped or fanned out.

Changes

  • Removed teamOrBUFanoutFrom usage from GetDimensionRankings, GetDimensionCostHistogram, and GetDimensionTokenHistogram. All rollup dimensions now use the raw logs table with a COALESCE(NULLIF(id_col, ''), 'unassigned') group expression.
  • Introduced isBucketedDimension to identify the five rollup dimensions (team, business unit, customer, user, virtual key) and bucketedIDExpr to produce the SQL expression that maps NULL/empty owners to the synthetic unassigned bucket.
  • TotalActualRequests and TotalAttributedRequests are now equal for all bucketed dimensions (single-owner attribution is additive), and both reflect the true org-wide request count including the Unassigned bucket.
  • Bucketed dimensions are forced onto the raw query path; the matview reader has no Unassigned bucket and is bypassed for these dimensions.
  • The attributed prop in DimensionRankingsTabView is now always true (all dimensions use the same attribution model), and the UI suppresses the raw unassigned ID sub-label in the rankings table.
  • Renamed TestDimensionRankings_ActualVsAttributedTotals to TestDimensionRankings_SingleOwnerAdditive and updated its assertions to reflect the new semantics. Added TestDimensionRankings_VirtualKeyUnassigned to cover the virtual key dimension.

Type of change

  • Refactor
  • Bug fix

Affected areas

  • Core (Go)
  • UI (React)

How to test

# Core
go test ./framework/logstore/... -run TestDimensionRankings
go test ./framework/logstore/... -run TestFilterBusinessUnitMatView

# UI
cd ui
pnpm i
pnpm build

Validate that the Teams dashboard tab sums per-team request counts to the org total, that requests with no scalar team owner appear under "Unassigned" rather than being dropped, and that no team is credited for a request it does not scalar-own.

Breaking changes

  • Yes

The fan-out attribution model is removed. Callers that previously relied on TotalAttributedRequests > TotalActualRequests to detect shared requests will now always see them equal. Per-team request counts will decrease for teams that previously received fan-out credit for shared requests.

Related issues

Security considerations

No auth, secrets, or PII changes. The bucketedIDExpr function interpolates only internal constant strings, not user input.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c586a4d-8337-47f0-8dd2-3b9677bb8cba

📥 Commits

Reviewing files that changed from the base of the PR and between 2b43dc5 and 80d0ad9.

📒 Files selected for processing (5)
  • framework/logstore/multi_team_matview_test.go
  • framework/logstore/rdb.go
  • framework/logstore/tables.go
  • ui/app/workspace/dashboard/components/dimensionRankingsTab.tsx
  • ui/app/workspace/dashboard/components/tabViews/dimensionRankingsTabView.tsx

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Dimension rankings now use single-owner attribution, ensuring totals reconcile with actual request counts.
    • Requests without an assigned owner are grouped under Unassigned.
    • Cost and token histograms now provide additive dimension breakdowns that reconcile with overall totals.
  • UI Improvements

    • Simplified labels for unassigned ranking entries by removing redundant identifiers.
    • Attribution display settings are now applied consistently across supported dimensions.

Walkthrough

Dimension rankings, cost histograms, and token histograms now use additive single-owner attribution. Missing or empty dimension ownership is grouped into an Unassigned bucket, with updated backend queries, tests, result documentation, and dashboard rendering.

Changes

Dimension rollup attribution

Layer / File(s) Summary
Bucketed rollup contract
framework/logstore/rdb.go, framework/logstore/tables.go
Defines the Unassigned bucket and documents additive actual and attributed request totals.
Dimension rankings aggregation
framework/logstore/rdb.go
Uses bucketed grouping, raw-table queries for bucketed dimensions, additive totals, aligned previous-period queries, and stable Unassigned display names.
Cost and token histogram aggregation
framework/logstore/rdb.go
Applies bucketed dimension expressions and groups raw-query results by the derived bucket value.
Rollup validation and dashboard presentation
framework/logstore/multi_team_matview_test.go, ui/app/workspace/dashboard/components/*
Tests single-owner and virtual-key attribution, verifies row totals reconcile, hides the synthetic identifier subtitle, and passes the attribution prop directly.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DimensionRankingsTabView
  participant GetDimensionRankings
  participant RawLogTable
  DimensionRankingsTabView->>GetDimensionRankings: request dimension rankings
  GetDimensionRankings->>RawLogTable: query bucketed dimension values
  RawLogTable-->>GetDimensionRankings: additive bucket totals
  GetDimensionRankings-->>DimensionRankingsTabView: rankings with Unassigned label
Loading

Suggested reviewers: akshaydeo, roroghost17

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: switching dimension rankings from fan-out attribution to single-owner additive rollups with an Unassigned bucket.
Description check ✅ Passed The description covers the required template sections and explains the change, testing, breaking change, and security notes; screenshots and related issues are the main gaps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 07-14-fix_unassigned_category_for_dashboard_charts
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch 07-14-fix_unassigned_category_for_dashboard_charts

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

impoiler commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Comment thread framework/logstore/rdb.go
Comment thread framework/logstore/rdb.go
@impoiler
impoiler force-pushed the 07-14-fix_unassigned_category_for_dashboard_charts branch from 6b015b3 to e85b397 Compare July 14, 2026 09:14
@impoiler
impoiler force-pushed the 07-14-fix_unassigned_category_for_dashboard_charts branch from e85b397 to 2b43dc5 Compare July 14, 2026 12:15
@impoiler impoiler self-assigned this Jul 14, 2026
@impoiler impoiler changed the title fix: Unassigned category for dashboard charts fix: replace fan-out attribution with single-owner additive rollup and "Unassigned" bucket for dimension rankings Jul 14, 2026
@impoiler
impoiler marked this pull request as ready for review July 14, 2026 16:16
@coderabbitai
coderabbitai Bot requested review from akshaydeo and roroghost17 July 14, 2026 16:17
@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
framework/logstore/rdb.go Replaces fan-out ranking and histogram aggregation with raw-log bucketed owner grouping.
framework/logstore/tables.go Updates the ranking result contract comments for additive rollup totals.
framework/logstore/multi_team_matview_test.go Updates ranking tests for single-owner team attribution and virtual-key Unassigned rows.
ui/app/workspace/dashboard/components/dimensionRankingsTab.tsx Hides the raw Unassigned id under the display label in the rankings table.
ui/app/workspace/dashboard/components/tabViews/dimensionRankingsTabView.tsx Passes attributed display mode for every dimension ranking tab.

Reviews (5): Last reviewed commit: "fix: Unassigned category for dashboard c..." | Re-trigger Greptile

Comment thread framework/logstore/rdb.go
Comment thread framework/logstore/rdb.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/logstore/rdb.go (1)

2184-2219: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

MAX(nameCol) can be NULL for the Unassigned bucket and break the scan into Name string.
When the grouped rows have no scalar owner, nameCol can be NULL for the whole bucket, so MAX(%s) returns NULL. Name is a plain string field, and the scan fails before the later unassignedDimensionName fallback runs. Wrap the aggregate with COALESCE(..., '').

Proposed fix
 	var nameExpr string
 	if nameCol != "" {
-		nameExpr = fmt.Sprintf("MAX(%s) as name", nameCol)
+		nameExpr = fmt.Sprintf("COALESCE(MAX(%s), '') as name", nameCol)
 	} else {
 		nameExpr = "'' as name"
 	}
🤖 Prompt for 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.

In `@framework/logstore/rdb.go` around lines 2184 - 2219, Update the nameExpr
construction in the currentResults query to wrap the MAX(nameCol) aggregate with
COALESCE and default NULL values to an empty string, while preserving the
existing alias and fallback for an empty nameCol.
🧹 Nitpick comments (1)
framework/logstore/rdb.go (1)

2230-2247: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Totals are only computed inside the if bucketed branch.

requestCounts.ActualRequests/AttributedRequests are left at their zero value whenever bucketed is false. Given isBucketedDimension currently covers every value DimensionColumnDef can return for RankingDimension (team/business_unit/customer/user/virtual_key), this branch appears unreachable today, but if a non-rollup RankingDimension is ever added, TotalActualRequests/TotalAttributedRequests would silently report 0 instead of an error or a real count.

🤖 Prompt for 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.

In `@framework/logstore/rdb.go` around lines 2230 - 2247, The request totals in
the ranking flow are only populated under the bucketed condition, leaving
non-bucketed dimensions with zero values. Update the logic around requestCounts
and the countQuery so ActualRequests and AttributedRequests are computed for
every supported dimension, or explicitly reject unsupported non-bucketed
dimensions instead of returning zero; preserve the existing filters and terminal
status conditions.
🤖 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.

Outside diff comments:
In `@framework/logstore/rdb.go`:
- Around line 2184-2219: Update the nameExpr construction in the currentResults
query to wrap the MAX(nameCol) aggregate with COALESCE and default NULL values
to an empty string, while preserving the existing alias and fallback for an
empty nameCol.

---

Nitpick comments:
In `@framework/logstore/rdb.go`:
- Around line 2230-2247: The request totals in the ranking flow are only
populated under the bucketed condition, leaving non-bucketed dimensions with
zero values. Update the logic around requestCounts and the countQuery so
ActualRequests and AttributedRequests are computed for every supported
dimension, or explicitly reject unsupported non-bucketed dimensions instead of
returning zero; preserve the existing filters and terminal status conditions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c6be40c7-f8bc-44d9-8c19-12a6de6369eb

📥 Commits

Reviewing files that changed from the base of the PR and between 35ab20e and 2b43dc5.

📒 Files selected for processing (5)
  • framework/logstore/multi_team_matview_test.go
  • framework/logstore/rdb.go
  • framework/logstore/tables.go
  • ui/app/workspace/dashboard/components/dimensionRankingsTab.tsx
  • ui/app/workspace/dashboard/components/tabViews/dimensionRankingsTabView.tsx

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 14, 2026

Pratham-Mishra04 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Merge activity

  • Jul 17, 11:05 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 17, 11:06 AM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 17, 11:06 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-14-fix_unassigned_category_for_dashboard_charts branch from 7e3f334 to 80d0ad9 Compare July 17, 2026 11:05
@Pratham-Mishra04
Pratham-Mishra04 merged commit b69c0a5 into dev Jul 17, 2026
14 of 15 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 07-14-fix_unassigned_category_for_dashboard_charts branch July 17, 2026 11:06
akshaydeo pushed a commit that referenced this pull request Jul 17, 2026
…d "Unassigned" bucket for dimension rankings (#5168)

## Summary

Replaces the multi-value fan-out attribution model for dimension rankings (Teams, Business Units, Customers, Users, Virtual Keys) with a single-owner additive model. Previously, a request shared across multiple teams was credited to every team it touched, causing double-counting and making per-team spend non-additive relative to the org total. Now each request is attributed to exactly one owner (the scalar ID column), and requests with no scalar owner collapse into a synthetic "Unassigned" bucket instead of being dropped or fanned out.

## Changes

- Removed `teamOrBUFanoutFrom` usage from `GetDimensionRankings`, `GetDimensionCostHistogram`, and `GetDimensionTokenHistogram`. All rollup dimensions now use the raw `logs` table with a `COALESCE(NULLIF(id_col, ''), 'unassigned')` group expression.
- Introduced `isBucketedDimension` to identify the five rollup dimensions (team, business unit, customer, user, virtual key) and `bucketedIDExpr` to produce the SQL expression that maps NULL/empty owners to the synthetic `unassigned` bucket.
- `TotalActualRequests` and `TotalAttributedRequests` are now equal for all bucketed dimensions (single-owner attribution is additive), and both reflect the true org-wide request count including the Unassigned bucket.
- Bucketed dimensions are forced onto the raw query path; the matview reader has no Unassigned bucket and is bypassed for these dimensions.
- The `attributed` prop in `DimensionRankingsTabView` is now always `true` (all dimensions use the same attribution model), and the UI suppresses the raw `unassigned` ID sub-label in the rankings table.
- Renamed `TestDimensionRankings_ActualVsAttributedTotals` to `TestDimensionRankings_SingleOwnerAdditive` and updated its assertions to reflect the new semantics. Added `TestDimensionRankings_VirtualKeyUnassigned` to cover the virtual key dimension.

## Type of change

- [x] Refactor
- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] UI (React)

## How to test

```sh
# Core
go test ./framework/logstore/... -run TestDimensionRankings
go test ./framework/logstore/... -run TestFilterBusinessUnitMatView

# UI
cd ui
pnpm i
pnpm build
```

Validate that the Teams dashboard tab sums per-team request counts to the org total, that requests with no scalar team owner appear under "Unassigned" rather than being dropped, and that no team is credited for a request it does not scalar-own.

## Breaking changes

- [x] Yes

The fan-out attribution model is removed. Callers that previously relied on `TotalAttributedRequests > TotalActualRequests` to detect shared requests will now always see them equal. Per-team request counts will decrease for teams that previously received fan-out credit for shared requests.

## Related issues

## Security considerations

No auth, secrets, or PII changes. The `bucketedIDExpr` function interpolates only internal constant strings, not user input.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akshaydeo pushed a commit that referenced this pull request Jul 18, 2026
…d "Unassigned" bucket for dimension rankings (#5168)

## Summary

Replaces the multi-value fan-out attribution model for dimension rankings (Teams, Business Units, Customers, Users, Virtual Keys) with a single-owner additive model. Previously, a request shared across multiple teams was credited to every team it touched, causing double-counting and making per-team spend non-additive relative to the org total. Now each request is attributed to exactly one owner (the scalar ID column), and requests with no scalar owner collapse into a synthetic "Unassigned" bucket instead of being dropped or fanned out.

## Changes

- Removed `teamOrBUFanoutFrom` usage from `GetDimensionRankings`, `GetDimensionCostHistogram`, and `GetDimensionTokenHistogram`. All rollup dimensions now use the raw `logs` table with a `COALESCE(NULLIF(id_col, ''), 'unassigned')` group expression.
- Introduced `isBucketedDimension` to identify the five rollup dimensions (team, business unit, customer, user, virtual key) and `bucketedIDExpr` to produce the SQL expression that maps NULL/empty owners to the synthetic `unassigned` bucket.
- `TotalActualRequests` and `TotalAttributedRequests` are now equal for all bucketed dimensions (single-owner attribution is additive), and both reflect the true org-wide request count including the Unassigned bucket.
- Bucketed dimensions are forced onto the raw query path; the matview reader has no Unassigned bucket and is bypassed for these dimensions.
- The `attributed` prop in `DimensionRankingsTabView` is now always `true` (all dimensions use the same attribution model), and the UI suppresses the raw `unassigned` ID sub-label in the rankings table.
- Renamed `TestDimensionRankings_ActualVsAttributedTotals` to `TestDimensionRankings_SingleOwnerAdditive` and updated its assertions to reflect the new semantics. Added `TestDimensionRankings_VirtualKeyUnassigned` to cover the virtual key dimension.

## Type of change

- [x] Refactor
- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] UI (React)

## How to test

```sh
# Core
go test ./framework/logstore/... -run TestDimensionRankings
go test ./framework/logstore/... -run TestFilterBusinessUnitMatView

# UI
cd ui
pnpm i
pnpm build
```

Validate that the Teams dashboard tab sums per-team request counts to the org total, that requests with no scalar team owner appear under "Unassigned" rather than being dropped, and that no team is credited for a request it does not scalar-own.

## Breaking changes

- [x] Yes

The fan-out attribution model is removed. Callers that previously relied on `TotalAttributedRequests > TotalActualRequests` to detect shared requests will now always see them equal. Per-team request counts will decrease for teams that previously received fan-out credit for shared requests.

## Related issues

## Security considerations

No auth, secrets, or PII changes. The `bucketedIDExpr` function interpolates only internal constant strings, not user input.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
…d "Unassigned" bucket for dimension rankings (maximhq#5168)

## Summary

Replaces the multi-value fan-out attribution model for dimension rankings (Teams, Business Units, Customers, Users, Virtual Keys) with a single-owner additive model. Previously, a request shared across multiple teams was credited to every team it touched, causing double-counting and making per-team spend non-additive relative to the org total. Now each request is attributed to exactly one owner (the scalar ID column), and requests with no scalar owner collapse into a synthetic "Unassigned" bucket instead of being dropped or fanned out.

## Changes

- Removed `teamOrBUFanoutFrom` usage from `GetDimensionRankings`, `GetDimensionCostHistogram`, and `GetDimensionTokenHistogram`. All rollup dimensions now use the raw `logs` table with a `COALESCE(NULLIF(id_col, ''), 'unassigned')` group expression.
- Introduced `isBucketedDimension` to identify the five rollup dimensions (team, business unit, customer, user, virtual key) and `bucketedIDExpr` to produce the SQL expression that maps NULL/empty owners to the synthetic `unassigned` bucket.
- `TotalActualRequests` and `TotalAttributedRequests` are now equal for all bucketed dimensions (single-owner attribution is additive), and both reflect the true org-wide request count including the Unassigned bucket.
- Bucketed dimensions are forced onto the raw query path; the matview reader has no Unassigned bucket and is bypassed for these dimensions.
- The `attributed` prop in `DimensionRankingsTabView` is now always `true` (all dimensions use the same attribution model), and the UI suppresses the raw `unassigned` ID sub-label in the rankings table.
- Renamed `TestDimensionRankings_ActualVsAttributedTotals` to `TestDimensionRankings_SingleOwnerAdditive` and updated its assertions to reflect the new semantics. Added `TestDimensionRankings_VirtualKeyUnassigned` to cover the virtual key dimension.

## Type of change

- [x] Refactor
- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] UI (React)

## How to test

```sh
# Core
go test ./framework/logstore/... -run TestDimensionRankings
go test ./framework/logstore/... -run TestFilterBusinessUnitMatView

# UI
cd ui
pnpm i
pnpm build
```

Validate that the Teams dashboard tab sums per-team request counts to the org total, that requests with no scalar team owner appear under "Unassigned" rather than being dropped, and that no team is credited for a request it does not scalar-own.

## Breaking changes

- [x] Yes

The fan-out attribution model is removed. Callers that previously relied on `TotalAttributedRequests > TotalActualRequests` to detect shared requests will now always see them equal. Per-team request counts will decrease for teams that previously received fan-out credit for shared requests.

## Related issues

## Security considerations

No auth, secrets, or PII changes. The `bucketedIDExpr` function interpolates only internal constant strings, not user input.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
…d "Unassigned" bucket for dimension rankings (maximhq#5168)

## Summary

Replaces the multi-value fan-out attribution model for dimension rankings (Teams, Business Units, Customers, Users, Virtual Keys) with a single-owner additive model. Previously, a request shared across multiple teams was credited to every team it touched, causing double-counting and making per-team spend non-additive relative to the org total. Now each request is attributed to exactly one owner (the scalar ID column), and requests with no scalar owner collapse into a synthetic "Unassigned" bucket instead of being dropped or fanned out.

## Changes

- Removed `teamOrBUFanoutFrom` usage from `GetDimensionRankings`, `GetDimensionCostHistogram`, and `GetDimensionTokenHistogram`. All rollup dimensions now use the raw `logs` table with a `COALESCE(NULLIF(id_col, ''), 'unassigned')` group expression.
- Introduced `isBucketedDimension` to identify the five rollup dimensions (team, business unit, customer, user, virtual key) and `bucketedIDExpr` to produce the SQL expression that maps NULL/empty owners to the synthetic `unassigned` bucket.
- `TotalActualRequests` and `TotalAttributedRequests` are now equal for all bucketed dimensions (single-owner attribution is additive), and both reflect the true org-wide request count including the Unassigned bucket.
- Bucketed dimensions are forced onto the raw query path; the matview reader has no Unassigned bucket and is bypassed for these dimensions.
- The `attributed` prop in `DimensionRankingsTabView` is now always `true` (all dimensions use the same attribution model), and the UI suppresses the raw `unassigned` ID sub-label in the rankings table.
- Renamed `TestDimensionRankings_ActualVsAttributedTotals` to `TestDimensionRankings_SingleOwnerAdditive` and updated its assertions to reflect the new semantics. Added `TestDimensionRankings_VirtualKeyUnassigned` to cover the virtual key dimension.

## Type of change

- [x] Refactor
- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] UI (React)

## How to test

```sh
# Core
go test ./framework/logstore/... -run TestDimensionRankings
go test ./framework/logstore/... -run TestFilterBusinessUnitMatView

# UI
cd ui
pnpm i
pnpm build
```

Validate that the Teams dashboard tab sums per-team request counts to the org total, that requests with no scalar team owner appear under "Unassigned" rather than being dropped, and that no team is credited for a request it does not scalar-own.

## Breaking changes

- [x] Yes

The fan-out attribution model is removed. Callers that previously relied on `TotalAttributedRequests > TotalActualRequests` to detect shared requests will now always see them equal. Per-team request counts will decrease for teams that previously received fan-out credit for shared requests.

## Related issues

## Security considerations

No auth, secrets, or PII changes. The `bucketedIDExpr` function interpolates only internal constant strings, not user input.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants