Skip to content

feat(web): add parent/child org scope to usage analytics#4254

Merged
RSO merged 7 commits into
mainfrom
incongruous-pilot
Jun 25, 2026
Merged

feat(web): add parent/child org scope to usage analytics#4254
RSO merged 7 commits into
mainfrom
incongruous-pilot

Conversation

@RSO

@RSO RSO commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

On the organization usage page, parent-org owners/billing managers can now switch the Scope between richer options instead of just My Usage / Entire Org:

  • My Usage — the caller's own usage in the parent org
  • All Organizations — org-wide aggregate across the parent + all child orgs
  • Parent Org Name — org-wide for the parent
  • Child Org 1 / Child Org 2 / … — org-wide for each child

Non-parent orgs keep the original two-option behavior; plain members still only see their own usage.

Changes

Server (usage-analytics-router.ts, usage-analytics-schemas.ts)

  • Added optional organizationIds to the usage filters (mutually exclusive with organizationId, always org-wide).
  • ensureScopeAccess: the multi-org path requires owner/billing_manager on every org in the list (parent owners inherit that on children).
  • buildScopeConditions: emits organization_id IN (…) for the aggregate, honoring user include/exclude filters without pinning to self.
  • New getScopeOrganizations endpoint returns the org + its children (owner/billing gated).
  • Generalized resolveOrgUsers to resolve user labels across multiple orgs.

Client (UsageAnalyticsDashboard.tsx, UsageAnalyticsSidebar.tsx, FilterGeneratorPopover.tsx, hooks.ts, useUsageDashboardState.ts)

  • Replaced the viewAs toggle state with a single orgScope value (self | all-orgs | <orgId>), persisted in the URL as scope.
  • Dashboard derives the effective organizationId / organizationIds / viewAs, clamps unknown/unauthorized scopes to self, and clears stale per-user filters when the scope org changes.
  • Threaded organizationIds through the filter-suggestion popover and user-label resolution.

Testing

  • pnpm typecheck — clean
  • oxlint on changed files — 0 warnings/errors
  • Jest: 9/9 pass, including 5 new tests for the all-orgs IN clause, precedence over single-org, user-filter handling, and self/org-wide/personal scoping.

Notes

  • "All Organizations" covers the parent + its direct children (the data model caps the hierarchy at two levels).
  • Per-org usage numbers come from the Snowflake rollups; this PR does not seed usage data.

On the organization usage page, parent-org owners/billing managers can now
switch the Scope between My Usage, an All Organizations aggregate (parent +
children), the parent org, and each child org. Adds a multi-org aggregate
mode to the usage filters/SQL, a getScopeOrganizations endpoint, and
generalizes resolveOrgUsers to span multiple orgs.
Comment thread apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx Outdated
Comment thread apps/web/src/components/usage-analytics/useUsageDashboardState.ts
@kilo-code-bot

kilo-code-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx
  • apps/web/src/routers/usage-analytics-router.test.ts
Previous Review Summaries (6 snapshots, latest commit 72ea8e1)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 72ea8e1)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 files)
  • apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx

Previous review (commit e63c07e)

Status: 1 Issue Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx 693 The new effectiveOrgId guard still renders AIAdoptionScoreCard and ActiveKiloclawsTable in My Usage, so the dashboard continues mixing org-wide panels into a self-scoped view.
Files Reviewed (1 files)
  • apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx - 1 issue

Previous review (commit c294502)

Status: 1 Issue Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/routers/usage-analytics-schemas.ts 49 The raised organizationIds cap still turns a legitimate large parent org into a broken All Organizations view once the page org plus children exceed 1,000, because the dashboard sends the full child list and org management does not enforce a matching hierarchy-size limit.
Files Reviewed (5 files)
  • apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx - 0 issues
  • apps/web/src/components/usage-analytics/useUsageDashboardState.ts - 0 issues
  • apps/web/src/routers/organizations/utils.ts - 0 issues
  • apps/web/src/routers/usage-analytics-router.ts - 0 issues
  • apps/web/src/routers/usage-analytics-schemas.ts - 1 issue

Previous review (commit 3b2c8a0)

Status: 2 Issues Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx 198 scopeListPending treats any missing scopeOrgs as loading, so a failed scope-list fetch keeps stale scope URLs active instead of collapsing back to self scope.
apps/web/src/routers/usage-analytics-schemas.ts 47 The new organizationIds cap breaks the All Organizations view for parent orgs with more than 99 children because the UI still sends the full child list and every analytics query is rejected by schema validation.
Files Reviewed (4 files)
  • apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx - 1 issue
  • apps/web/src/routers/usage-analytics-router.ts - 0 issues
  • apps/web/src/routers/usage-analytics-router.test.ts - 0 issues
  • apps/web/src/routers/usage-analytics-schemas.ts - 1 issue

Previous review (commit ec36fcd)

Status: 1 Issue Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx 198 scopeListPending treats any missing scopeOrgs as loading, so a failed scope-list fetch keeps stale scope URLs active instead of collapsing back to self scope.
Files Reviewed (2 files)
  • apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx - 1 issue
  • apps/web/src/routers/usage-analytics-router.ts - 0 issues

Previous review (commit 64e92af)

Status: 2 Issues Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx 194 Deep-linked child/all-org scopes are clamped to self until scope organizations load, which can clear valid user filters and briefly query the wrong data.
apps/web/src/components/usage-analytics/useUsageDashboardState.ts 212 Legacy viewAs=org-wide URLs are no longer parsed, so existing shared/bookmarked org-wide links fall back to self scope.
Files Reviewed (8 files)
  • apps/web/src/components/usage-analytics/FilterGeneratorPopover.tsx - 0 issues
  • apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx - 1 issue
  • apps/web/src/components/usage-analytics/UsageAnalyticsSidebar.tsx - 0 issues
  • apps/web/src/components/usage-analytics/hooks.ts - 0 issues
  • apps/web/src/components/usage-analytics/useUsageDashboardState.ts - 1 issue
  • apps/web/src/routers/usage-analytics-router.test.ts - 0 issues
  • apps/web/src/routers/usage-analytics-router.ts - 0 issues
  • apps/web/src/routers/usage-analytics-schemas.ts - 0 issues

Reviewed by gpt-5.4-20260305 · Input: 60.1K · Output: 4.1K · Cached: 237.1K

Review guidance: REVIEW.md from base branch main

- Honor a deep-linked org scope while getScopeOrganizations is still loading
  so the cleanup effect no longer wipes grouping/user-filter state before
  validation runs.
- Exclude soft-deleted child orgs from the scope list and the All
  Organizations aggregate.
Comment thread apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx Outdated
The caller-controlled organizationIds filter drives per-org authorization
queries and the Snowflake IN clause, so bound it at the API boundary
(MAX_SCOPE_ORGANIZATION_IDS) to prevent resource exhaustion.
Comment thread apps/web/src/routers/usage-analytics-schemas.ts
RSO added 2 commits June 25, 2026 13:54
- Replace per-org authorization fan-out with batched helpers
  (getOrganizationsAccessRoles / ensureOrganizationsAccess) so the
  multi-org scope and resolveOrgUsers use a fixed number of queries.
- Raise MAX_SCOPE_ORGANIZATION_IDS to 1000 so large parent hierarchies can
  use All Organizations without hitting the validation cap.
- Migrate legacy `?viewAs=org-wide` deep links to the new `scope` model so
  they keep opening an org-wide view instead of collapsing to My Usage.
AIAdoptionScoreCard and ActiveKiloclawsTable now follow the selected single
org instead of always the page org, and are hidden in the All Organizations
aggregate (which they cannot represent), so the dashboard no longer mixes
scopes when an admin switches to a child org.
Comment thread apps/web/src/components/usage-analytics/UsageAnalyticsDashboard.tsx
RSO added 2 commits June 25, 2026 14:18
Derive scopeListPending from the query's isLoading state instead of the
absence of data, so a failed getScopeOrganizations request falls back to
clamping the scope (to My Usage) rather than honoring a stale/unknown URL
scope indefinitely.
@RSO RSO merged commit c81775b into main Jun 25, 2026
20 checks passed
@RSO RSO deleted the incongruous-pilot branch June 25, 2026 12:50
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.

2 participants