feat: add seat tracking infrastructure for monthly proration#27001
feat: add seat tracking infrastructure for monthly proration#27001
Conversation
Add seat change logging infrastructure with operationId for idempotency. This PR adds the foundation for monthly proration billing by tracking seat additions and removals, gated behind the monthly-proration feature flag. - Add operationId field to SeatChangeLog for idempotency - Update SeatChangeLogRepository to support upsert with operationId - Add feature flag guard in SeatChangeTrackingService - Integrate seat tracking in team member invites - Integrate seat tracking in bulk user deletions - Integrate seat tracking in team service operations - Integrate seat tracking in DSYNC user creation When monthly-proration feature flag is disabled, seat logging is skipped and behavior remains unchanged. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
2 issues found across 12 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/trpc/server/routers/viewer/organizations/utils.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/organizations/utils.ts:128">
P2: This counts seats by filtering `membershipData` for every team, which makes the new seat-tracking logic O(n^2) for bulk invites. Precompute seat counts in one pass and then map teams to counts to keep it O(n).</violation>
</file>
<file name="packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts:420">
P2: Pass an idempotency `operationId` into the new seat tracking calls so retries don’t double-count seat additions.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const additionsByTeam = Array.from(topLevelTeamIds) | ||
| .map((teamId) => ({ | ||
| teamId, | ||
| seatCount: membershipData.filter((entry) => entry.teamId === teamId).length, | ||
| })) | ||
| .filter((entry) => entry.seatCount > 0); |
There was a problem hiding this comment.
P2: This counts seats by filtering membershipData for every team, which makes the new seat-tracking logic O(n^2) for bulk invites. Precompute seat counts in one pass and then map teams to counts to keep it O(n).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/trpc/server/routers/viewer/organizations/utils.ts, line 128:
<comment>This counts seats by filtering `membershipData` for every team, which makes the new seat-tracking logic O(n^2) for bulk invites. Precompute seat counts in one pass and then map teams to counts to keep it O(n).</comment>
<file context>
@@ -106,6 +123,26 @@ export const addMembersToTeams = async ({ user, input }: AddBulkToTeamProps) =>
+ if (topLevelTeamIds.size > 0 && membershipData.length > 0) {
+ const seatTracker = new SeatChangeTrackingService();
+ const additionsByTeam = Array.from(topLevelTeamIds)
+ .map((teamId) => ({
+ teamId,
</file context>
Fix confidence (alpha): 8/10
| const additionsByTeam = Array.from(topLevelTeamIds) | |
| .map((teamId) => ({ | |
| teamId, | |
| seatCount: membershipData.filter((entry) => entry.teamId === teamId).length, | |
| })) | |
| .filter((entry) => entry.seatCount > 0); | |
| const seatCountsByTeam = membershipData.reduce((acc, { teamId }) => { | |
| acc.set(teamId, (acc.get(teamId) ?? 0) + 1); | |
| return acc; | |
| }, new Map()); | |
| const additionsByTeam = Array.from(topLevelTeamIds) | |
| .map((teamId) => ({ | |
| teamId, | |
| seatCount: seatCountsByTeam.get(teamId) ?? 0, | |
| })) | |
| .filter((entry) => entry.seatCount > 0); |
| if (createdUsers.length > 0) { | ||
| const seatTracker = new SeatChangeTrackingService(); | ||
| const trackingTeamId = parentId ?? teamId; | ||
| await seatTracker.logSeatAddition({ |
There was a problem hiding this comment.
P2: Pass an idempotency operationId into the new seat tracking calls so retries don’t double-count seat additions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts, line 420:
<comment>Pass an idempotency `operationId` into the new seat tracking calls so retries don’t double-count seat additions.</comment>
<file context>
@@ -415,6 +413,16 @@ export async function createNewUsersConnectToOrgIfExists({
+ if (createdUsers.length > 0) {
+ const seatTracker = new SeatChangeTrackingService();
+ const trackingTeamId = parentId ?? teamId;
+ await seatTracker.logSeatAddition({
+ teamId: trackingTeamId,
+ seatCount: createdUsers.length,
</file context>
Devin AI is addressing Cubic AI's review feedbackA Devin session has been created to address the issues identified by Cubic AI. |
|
I reviewed the Cubic AI feedback and checked the confidence scores for each issue:
Per the guidelines to only fix issues with confidence scores of 9/10 or higher, no changes were made to this PR. Both issues are below the threshold. |
emrysal
left a comment
There was a problem hiding this comment.
I like this, looked at the cubic review and don't think it's critical for perf optimisation in this case.
Part 1 of monthly proration feature.
Adds seat change tracking infrastructure with operationId for idempotency.
Dependency chain: