Skip to content

fix(api): scope DevPass aggregates by org.kind - #2762

Merged
steebchen merged 2 commits into
mainfrom
filter-devpass-queries-by-org-kind
Jun 20, 2026
Merged

steebchen merged 2 commits into
mainfrom
filter-devpass-queries-by-org-kind

Conversation

@steebchen

@steebchen steebchen commented Jun 19, 2026 •

Copy link
Copy Markdown
Member

Summary

The broader DevPass aggregate queries in apps/api/src/routes/admin.ts — the KPI strip, the /devpass/timeseries endpoint, and the /devpass/usage endpoint — identified DevPass orgs by inferring membership from devPlan != 'none' or from transaction history (dev_plan_* / legacy subscription_* rows). This was flagged during the kind rename PR (#2743) as a pre-existing analytics-scoping concern.

That heuristic had two problems:

  • Churned subscribers were dropped from cost/usage aggregates once devPlan reset to 'none' (unless they still had matching transaction history).
  • Org Pro revenue could leak in, since legacy subscription_* transaction types are still written for non-personal org Pro subscriptions.

This PR scopes every DevPass aggregate by the authoritative organization.kind = 'devpass' column, which is set at org creation and stays stable across the subscription lifecycle (the cancel path only clears devPlan, never kind). The subscriber list and detail handlers already used kind; this brings the aggregates in line.

Changes

All in apps/api/src/routes/admin.ts:

  • KPI queries (activeRows, churnedRow, startsRow, endsRow, refundsRow, utilRow, universeRow): add eq(kind, 'devpass') (joining organization where the query didn't already).
  • Timeseries (oldest range anchor, revenuePerDay, refundsPerDay, costPerDay): replace the or(dev_plan types, and(legacy, kind)) / EXISTS(...) patterns with a plain kind = 'devpass' filter.
  • Usage (devpassOrgFilter): replace the devPlan != 'none' OR EXISTS(...) heuristic with kind = 'devpass', dropping the correlated subquery in favor of a plain indexed filter.

Testing

  • pnpm format
  • pnpm turbo run build --filter=api ✅

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Improved DevPass reporting by ensuring all subscriber KPI and monthly metrics are consistently scoped to DevPass organizations.
    • Updated DevPass timeseries calculations to apply the same organization scoping across revenue, refunds, usage, and cost views.
    • Simplified DevPass usage filtering and streamlined refund-type handling to improve consistency in reported results.

The DevPass KPI, timeseries, and usage aggregate queries in admin.ts
inferred DevPass membership from `devPlan != 'none'` or transaction
history (dev_plan_* / legacy subscription_* rows). That heuristic missed
churned orgs (devPlan reset to 'none') and risked misattributing org Pro
`subscription_*` revenue as DevPass.

Scope every aggregate by the authoritative `organization.kind = 'devpass'`
column, which is stable across the subscription lifecycle, so churned
subscribers stay counted and non-DevPass orgs are excluded. This also
drops the correlated EXISTS subqueries in favor of a plain indexed filter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dca45cc9-919b-46b5-aa16-48ce3dddb171

📥 Commits

Reviewing files that changed from the base of the PR and between 7664280 and e080027.

📒 Files selected for processing (1)
  • apps/api/src/routes/admin.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/routes/admin.ts

Walkthrough

All DevPass admin reporting queries in apps/api/src/routes/admin.ts are updated to filter by organization.kind = 'devpass' directly, replacing prior OR/EXISTS predicates based on devPlan history. Affected endpoints are the DevPass KPI strip, /devpass/timeseries, and /devpass/usage.

Changes

DevPass Query Scoping

Layer / File(s) Summary
DevPass KPI strip scoping
apps/api/src/routes/admin.ts
Active/cancelling base, churn, starts/ends this month, refunds, weighted utilization, and cycle-universe subqueries all gain an organization.kind = 'devpass' guard. credit_refund typing is simplified to a direct inArray over DevPlan and legacy DevPlan transaction type sets, removing the prior conditional structure.
/devpass/timeseries revenue, refund, and cost scoping
apps/api/src/routes/admin.ts
Chart range and revenue aggregation are scoped by organization.kind = 'devpass'; legacy subscription_* types are included via a single inArray gated on org kind, replacing earlier OR combinations. Refund-per-day adds org kind and originalTx.type membership checks; cost-per-day switches from a "current or historical DevPass plan" predicate to organization.kind = 'devpass'.
/devpass/usage org filter simplification
apps/api/src/routes/admin.ts
The DevPass org predicate is changed from an EXISTS/devPlan != 'none' check to a direct eq(tables.organization.kind, 'devpass') filter.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • theopenco/llmgateway#2085: Introduced the DevPass admin endpoints whose KPI, timeseries, and usage query logic this PR directly modifies.
  • theopenco/llmgateway#2400: Added the "refunds this month" KPI aggregation in the same DevPass KPI strip that this PR refactors to use organization.kind = 'devpass' scoping.
  • theopenco/llmgateway#2543: Added all-time revenue/cost/margin subqueries to the DevPass subscriber and detail handlers that this PR now rescopes to organization.kind = 'devpass'.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: scoping DevPass aggregates by the organization.kind column instead of heuristics.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 filter-devpass-queries-by-org-kind

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 and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7664280f57

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)!;
// Filter: only DevPass orgs (kind = 'devpass'). Stable across the
// subscription lifecycle, so churned orgs (devPlan = 'none') stay included.
const devpassOrgFilter = eq(tables.organization.kind, "devpass");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve subscription-history guard on DevPass usage

When a code-app signup or /dev-plans/personal-org call runs, getOrCreatePersonalOrg() creates a kind='devpass' org before any subscription while devPlan is still none; the previous predicate also required an active plan or a start transaction. With this kind-only filter, /devpass/usage (and the same kind-only cost filter above) can include rollups from never-subscribed personal orgs if they have project usage from migrated/personal credits, inflating DevPass usage/cost. Keep the kind check but retain a current-or-history DevPass predicate.

Useful? React with 👍 / 👎.

@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.

Actionable comments posted: 2

🤖 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 `@apps/api/src/routes/admin.ts`:
- Around line 10040-10045: The WHERE clause in the query is filtering for only
"dev_plan_start" transaction types, which excludes legacy "subscription_start"
rows from the churned KPI count. Modify the condition that checks
`tables.transaction.type` to include both the new "dev_plan_start" type and the
legacy "subscription_start" type so that pre-rename transaction rows are counted
in the churned KPI calculation. Replace the single equality check with a
condition that accepts multiple transaction type values.
- Around line 10152-10157: The WHERE clause in the query for totalMrrCycle and
totalRealCostCycle calculations is missing a filter to exclude expired
subscriptions. Currently it only filters for organization.kind equal to
"devpass" and devPlan not equal to "none", but this includes expired
organizations unlike the activeRows and utilRow calculations. Add an additional
condition to the and() clause that filters out expired subscriptions using the
same expiration logic used in the activeRows and utilRow queries to ensure all
KPI metrics use consistent filtering criteria.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b2c49622-01fa-433b-b8aa-8570ec91dbb7

📥 Commits

Reviewing files that changed from the base of the PR and between 3e7588b and 7664280.

📒 Files selected for processing (1)
  • apps/api/src/routes/admin.ts

Comment on lines 10040 to 10045
.where(
and(
eq(tables.organization.kind, "devpass"),
eq(tables.transaction.type, "dev_plan_start"),
eq(tables.organization.devPlan, "none"),
),

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include legacy starts in the churned KPI.

Now that this query is gated by organization.kind = "devpass", pre-rename subscription_start rows are safe to count here too. As written, legacy churned DevPass orgs can appear in the subscriber list via subscribedSinceSub, but are omitted from kpis.churned.

Proposed fix
 				eq(tables.organization.kind, "devpass"),
-				eq(tables.transaction.type, "dev_plan_start"),
+				inArray(tables.transaction.type, [
+					"dev_plan_start",
+					"subscription_start",
+				]),
 				eq(tables.organization.devPlan, "none"),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.where(
and(
eq(tables.organization.kind, "devpass"),
eq(tables.transaction.type, "dev_plan_start"),
eq(tables.organization.devPlan, "none"),
),
.where(
and(
eq(tables.organization.kind, "devpass"),
inArray(tables.transaction.type, [
"dev_plan_start",
"subscription_start",
]),
eq(tables.organization.devPlan, "none"),
),
🤖 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 `@apps/api/src/routes/admin.ts` around lines 10040 - 10045, The WHERE clause in
the query is filtering for only "dev_plan_start" transaction types, which
excludes legacy "subscription_start" rows from the churned KPI count. Modify the
condition that checks `tables.transaction.type` to include both the new
"dev_plan_start" type and the legacy "subscription_start" type so that
pre-rename transaction rows are counted in the churned KPI calculation. Replace
the single equality check with a condition that accepts multiple transaction
type values.

Comment thread apps/api/src/routes/admin.ts
universeRow (totalMrrCycle/totalRealCostCycle) is documented as the
active subscriber universe but lacked the expiration filter that
activeRows and utilRow apply, so expired-but-not-yet-churned orgs
inflated the KPI cycle totals. Add the same
`expiresAt IS NULL OR expiresAt > NOW()` guard for consistency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@steebchen
steebchen added this pull request to the merge queue Jun 20, 2026
Merged via the queue into main with commit 2bf78b7 Jun 20, 2026
12 checks passed
@steebchen
steebchen deleted the filter-devpass-queries-by-org-kind branch June 20, 2026 17:02
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.

1 participant