feat(webapp): self serve preview branches and team members#3201
feat(webapp): self serve preview branches and team members#3201
Conversation
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a generic SearchInput component (renamed from LogsSearchInput) with an optional Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 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 |
…ss-route error handling, and fix open redirect vulnerability.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
apps/webapp/app/v3/services/setSeatsAddOn.server.ts (1)
2-2: Use a@trigger.dev/coresubpath import here.This new root import violates the webapp package-boundary rule; the same pattern was also added in
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx.As per coding guidelines,
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/webapp/app/v3/services/setSeatsAddOn.server.ts` at line 2, Replace the root import of tryCatch from "@trigger.dev/core" with the approved package subpath export defined in `@trigger.dev/core`'s package.json (use the webapp-approved subpath, e.g. the server/runtime subpath provided by the package) so the webapp package-boundary rule is respected; update the import that brings in tryCatch (the symbol tryCatch in setSeatsAddOn.server.ts) to import from the proper "@trigger.dev/core/..." subpath and do the same change in the other occurrence noted (route.tsx) to keep imports consistent.apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx (1)
37-37: ExtractPurchaseSeatsModalinto a shared component module.Importing a presentational component from a route module that owns loaders, actions, and server-only imports (db.server, session.server, member.server, message.server, setSeatsAddOn.server) creates a brittle cross-route dependency. Move the modal to a dedicated component or presenter module under
~/components/or~/presenters/instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.invite/route.tsx at line 37, The PurchaseSeatsModal component is being imported from a route module that includes server-only logic, creating a brittle cross-route dependency; extract PurchaseSeatsModal into a pure presentational module (e.g., ~/components/PurchaseSeatsModal or ~/presenters/PurchaseSeatsModal) so it no longer pulls in loaders/actions or server-only imports. Move the JSX and any client-side handlers into the new file, export a prop-driven component (no direct db.server/session.server/member.server/message.server/setSeatsAddOn.server usage), update both the existing import in _app.orgs.$organizationSlug.settings.team/route and the import in _app.orgs.$organizationSlug.invite/route to import the new shared module, and adapt callers to pass necessary data/handlers (or create small thin adapter wrappers in their routes) so server interactions remain in the route files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.team/route.tsx:
- Around line 624-625: The form's submit target is hardcoded to
organizationTeamPath(organization) in PurchaseSeatsModal via fetcher.Form; make
the form action configurable by adding an action prop (e.g., action?: string) to
PurchaseSeatsModal and using that prop for fetcher.Form's action, defaulting to
organizationTeamPath(organization) when no action is provided, then update the
invite route (_app.orgs.$organizationSlug.invite/route.tsx) to pass the invite
flow's post target into PurchaseSeatsModal so successful purchases return to the
invite flow instead of redirecting to the team page.
- Around line 542-554: The icon-only submit Button (LeadingIcon={NoSymbolIcon})
lacks an accessible name; add an accessible name to the Button used inside
SimpleTooltip so screen readers announce its purpose (e.g., add
aria-label="Revoke invite" or include a visually-hidden text child). Update the
Button props in the SimpleTooltip child (the Button component) to provide that
label so the control is accessible while keeping the icon and tooltip.
- Around line 101-110: PurchaseSchema currently only enforces minimum numeric
values and the route handler forwards the parsed amount directly to the service,
allowing crafted POSTs to bypass business rules enforced in PurchaseSeatsModal;
update the server-side validation and enforcement: (1) tighten PurchaseSchema to
require whole-seat integers (use z.coerce.number().int() and appropriate .min()
per action) and (2) add explicit checks in the route POST handler (the code that
parses PurchaseSchema and calls the service) to enforce plan eligibility,
prevent non-whole-seat quantities, and when reducing seats require removal of
members/invites or reject the request—only after these checks pass call the
existing service method (the same call currently forwarding amount) to apply the
change.
In `@apps/webapp/app/v3/services/setSeatsAddOn.server.ts`:
- Around line 25-29: The service's call method performs billing mutations
(setSeatsAddOn) and may create quota requests without checking that userId is
authorized for organizationId; before calling setSeatsAddOn or sending any
quota-increase request, validate that userId belongs to organizationId and has
billing/admin rights (e.g., call your existing org auth helpers such as
requireUserInOrganization or requireOrgAdmin/requireBillingPermission) and
return a 403/throw if unauthorized; insert this check at the top of the async
call({ userId, organizationId, ... }) function so both the setSeatsAddOn branch
and the quota-request branch (lines ~57–87) are protected.
---
Nitpick comments:
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.invite/route.tsx:
- Line 37: The PurchaseSeatsModal component is being imported from a route
module that includes server-only logic, creating a brittle cross-route
dependency; extract PurchaseSeatsModal into a pure presentational module (e.g.,
~/components/PurchaseSeatsModal or ~/presenters/PurchaseSeatsModal) so it no
longer pulls in loaders/actions or server-only imports. Move the JSX and any
client-side handlers into the new file, export a prop-driven component (no
direct
db.server/session.server/member.server/message.server/setSeatsAddOn.server
usage), update both the existing import in
_app.orgs.$organizationSlug.settings.team/route and the import in
_app.orgs.$organizationSlug.invite/route to import the new shared module, and
adapt callers to pass necessary data/handlers (or create small thin adapter
wrappers in their routes) so server interactions remain in the route files.
In `@apps/webapp/app/v3/services/setSeatsAddOn.server.ts`:
- Line 2: Replace the root import of tryCatch from "@trigger.dev/core" with the
approved package subpath export defined in `@trigger.dev/core`'s package.json (use
the webapp-approved subpath, e.g. the server/runtime subpath provided by the
package) so the webapp package-boundary rule is respected; update the import
that brings in tryCatch (the symbol tryCatch in setSeatsAddOn.server.ts) to
import from the proper "@trigger.dev/core/..." subpath and do the same change in
the other occurrence noted (route.tsx) to keep imports consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: fe290cba-f28e-4dec-a4d2-ad756323a56d
📒 Files selected for processing (5)
apps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/services/platform.v3.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/webapp/app/services/platform.v3.server.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: typecheck / typecheck
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: In TypeScript SDK usage, always import from@trigger.dev/sdk, never from@trigger.dev/sdk/v3or use deprecated client.defineJob
Import from@trigger.dev/coresubpaths only, never from the root
Use the Run Engine 2.0 (@internal/run-engine) and redis-worker for all new work, not legacy V1 MarQS queue or deprecated V1 functions
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
apps/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.ts
apps/webapp/**/*.server.ts
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Access environment variables via the
envexport fromapp/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/presenters/TeamPresenter.server.tsapps/webapp/app/v3/services/setSeatsAddOn.server.ts
apps/webapp/app/v3/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Organize services in the webapp following the pattern
app/v3/services/*/*.server.ts
Files:
apps/webapp/app/v3/services/setSeatsAddOn.server.ts
apps/webapp/app/v3/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
In apps/webapp/app/v3/ directory, only modify V2 code paths - V1/V2 branching should only have V2 modified, as V1 code is legacy
Files:
apps/webapp/app/v3/services/setSeatsAddOn.server.ts
apps/webapp/app/v3/services/**/*.server.ts
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
When editing services that branch on
RunEngineVersionto support both V1 and V2 (e.g.,cancelTaskRun.server.ts,batchTriggerV3.server.ts), only modify V2 code paths
Files:
apps/webapp/app/v3/services/setSeatsAddOn.server.ts
🧠 Learnings (6)
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/presenters/**/*.server.{ts,tsx} : Organize presenters in the webapp following the pattern `app/v3/presenters/*/*.server.ts` to move complex loader code into classes
Applied to files:
apps/webapp/app/presenters/TeamPresenter.server.ts
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
| const PurchaseSchema = z.discriminatedUnion("action", [ | ||
| z.object({ | ||
| action: z.literal("purchase"), | ||
| amount: z.coerce.number().min(0, "Amount must be 0 or more"), | ||
| }), | ||
| z.object({ | ||
| action: z.literal("quota-increase"), | ||
| amount: z.coerce.number().min(1, "Amount must be greater than 0"), | ||
| }), | ||
| ]); |
There was a problem hiding this comment.
Don’t rely on the modal state to enforce seat-purchase rules.
Line 104/108 only validate a minimum, and Lines 138-145 forward the parsed amount straight to the service. Right now the checks for plan eligibility, whole-seat quantities, and “remove members/invites before reducing seats” live in PurchaseSeatsModal, so a crafted POST can bypass this route’s own business rules.
Also applies to: 132-145
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.team/route.tsx
around lines 101 - 110, PurchaseSchema currently only enforces minimum numeric
values and the route handler forwards the parsed amount directly to the service,
allowing crafted POSTs to bypass business rules enforced in PurchaseSeatsModal;
update the server-side validation and enforcement: (1) tighten PurchaseSchema to
require whole-seat integers (use z.coerce.number().int() and appropriate .min()
per action) and (2) add explicit checks in the route POST handler (the code that
parses PurchaseSchema and calls the service) to enforce plan eligibility,
prevent non-whole-seat quantities, and when reducing seats require removal of
members/invites or reject the request—only after these checks pass call the
existing service method (the same call currently forwarding amount) to apply the
change.
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
Show resolved
Hide resolved
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.team/route.tsx:
- Around line 126-149: The org lookup before calling SetSeatsAddOnService lacks
an authorization check, so any authenticated user can change seats by slug;
before constructing/awaiting service.call in the "purchase-seats" branch, verify
the user's membership and billing-admin role for the resolved org (reuse the
existing org-access check logic used elsewhere, e.g., the helper that validates
membership/roles) or delegate this enforcement into SetSeatsAddOnService; ensure
you perform the check using userId and org.id (the same values passed to
service.call), and return an appropriate json({ ok: false, error: ... }) when
the user is not authorized.
- Line 13: Update the import for tryCatch to use the utilities subpath: replace
the root import of tryCatch (import { tryCatch } from "@trigger.dev/core") with
the subpath import (import { tryCatch } from "@trigger.dev/core/utils") in the
module that defines the route (the file containing the tryCatch import for this
org settings team route).
- Around line 573-583: The prop type for triggerButton is too permissive
(React.ReactNode) and can allow fragments, strings, arrays, or null which break
Radix's Slot used by DialogTrigger with asChild; update the prop signature to
require a single element by changing triggerButton?: React.ReactNode to
triggerButton?: React.ReactElement (or non-optional React.ReactElement if
required) in the component props where seatPricing, extraSeats, usedSeats,
maxQuota, planSeatLimit and triggerButton are defined, ensuring consumers pass
exactly one React element to DialogTrigger with asChild.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 9b11b9fe-7abf-4e50-ad87-90c646f94261
📒 Files selected for processing (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: typecheck / typecheck
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: In TypeScript SDK usage, always import from@trigger.dev/sdk, never from@trigger.dev/sdk/v3or use deprecated client.defineJob
Import from@trigger.dev/coresubpaths only, never from the root
Use the Run Engine 2.0 (@internal/run-engine) and redis-worker for all new work, not legacy V1 MarQS queue or deprecated V1 functions
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
apps/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
🧠 Learnings (5)
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
Outdated
Show resolved
Hide resolved
| if (formType === "purchase-seats") { | ||
| const org = await $replica.organization.findFirst({ | ||
| where: { slug: organizationSlug }, | ||
| select: { id: true }, | ||
| }); | ||
|
|
||
| if (!org) { | ||
| return json({ ok: false, error: "Organization not found" } as const); | ||
| } | ||
|
|
||
| const submission = parse(formData, { schema: PurchaseSchema }); | ||
|
|
||
| if (!submission.value || submission.intent !== "submit") { | ||
| return json(submission); | ||
| } | ||
|
|
||
| const service = new SetSeatsAddOnService(); | ||
| const [error, result] = await tryCatch( | ||
| service.call({ | ||
| userId, | ||
| organizationId: org.id, | ||
| action: submission.value.action, | ||
| amount: submission.value.amount, | ||
| }) |
There was a problem hiding this comment.
Authorize seat changes before calling the billing service.
This branch only resolves the org by slug and then forwards organizationId into SetSeatsAddOnService. The new service path does not enforce membership or billing-admin checks either, so any authenticated user who knows another org's slug can change its seat add-on or trigger a quota request. Please gate this with the same org-access check used elsewhere, or move the authorization check into SetSeatsAddOnService so every caller is protected.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.team/route.tsx
around lines 126 - 149, The org lookup before calling SetSeatsAddOnService lacks
an authorization check, so any authenticated user can change seats by slug;
before constructing/awaiting service.call in the "purchase-seats" branch, verify
the user's membership and billing-admin role for the resolved org (reuse the
existing org-access check logic used elsewhere, e.g., the helper that validates
membership/roles) or delegate this enforcement into SetSeatsAddOnService; ensure
you perform the check using userId and org.id (the same values passed to
service.call), and return an appropriate json({ ok: false, error: ... }) when
the user is not authorized.
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx (1)
342-349:⚠️ Potential issue | 🟡 MinorGuard against division by zero in progress calculations.
If
limits.limitis ever 0 (e.g., edge case during data loading or misconfigured plan), these calculations would produceInfinityorNaN, causing incorrect SVG rendering.🛡️ Proposed fix
<circle className={`fill-none ${requiresUpgrade ? "stroke-error" : "stroke-success"}`} strokeWidth="4" r="10" cx="12" cy="12" - strokeDasharray={`${(limits.used / limits.limit) * 62.8} 62.8`} + strokeDasharray={`${limits.limit > 0 ? (limits.used / limits.limit) * 62.8 : 0} 62.8`} strokeDashoffset="0" strokeLinecap="round" /> </svg> </div> } - content={`${Math.round((limits.used / limits.limit) * 100)}%`} + content={`${limits.limit > 0 ? Math.round((limits.used / limits.limit) * 100) : 0}%`} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.team/route.tsx around lines 342 - 349, The progress calculations use limits.used / limits.limit directly which can produce Infinity/NaN when limits.limit is 0; update the expressions in the SVG strokeDasharray and the content percent (references: strokeDasharray={`${(limits.used / limits.limit) * 62.8} 62.8`} and content={`${Math.round((limits.used / limits.limit) * 100)}%`}) to guard against division-by-zero by using a safe denominator (e.g., denom = limits.limit > 0 ? limits.limit : 1), compute the ratio = Math.min(Math.max(limits.used / denom, 0), 1) and then use ratio * 62.8 for strokeDasharray and Math.round(ratio * 100) + '%' for content so the SVG and displayed percent remain valid.
🧹 Nitpick comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx (1)
655-655: Remove unused variable.The
organizationvariable is declared but never used in this component.🧹 Proposed fix
function PurchaseBranchesModal({ ... }) { const lastSubmission = useActionData(); - const organization = useOrganization(); const [form, { amount }] = useForm({🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx at line 655, The variable "organization" is declared via useOrganization() but never used; remove the unused declaration to clean up the component—delete the line containing "const organization = useOrganization();" (or if you intended to use it, reference "organization" where needed); ensure no other code depends on useOrganization() in this file (search for useOrganization and organization) before removing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.team/route.tsx:
- Around line 342-349: The progress calculations use limits.used / limits.limit
directly which can produce Infinity/NaN when limits.limit is 0; update the
expressions in the SVG strokeDasharray and the content percent (references:
strokeDasharray={`${(limits.used / limits.limit) * 62.8} 62.8`} and
content={`${Math.round((limits.used / limits.limit) * 100)}%`}) to guard against
division-by-zero by using a safe denominator (e.g., denom = limits.limit > 0 ?
limits.limit : 1), compute the ratio = Math.min(Math.max(limits.used / denom,
0), 1) and then use ratio * 62.8 for strokeDasharray and Math.round(ratio * 100)
+ '%' for content so the SVG and displayed percent remain valid.
---
Nitpick comments:
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx:
- Line 655: The variable "organization" is declared via useOrganization() but
never used; remove the unused declaration to clean up the component—delete the
line containing "const organization = useOrganization();" (or if you intended to
use it, reference "organization" where needed); ensure no other code depends on
useOrganization() in this file (search for useOrganization and organization)
before removing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 53ed5d07-80d8-4b21-bd60-31c124dd1422
📒 Files selected for processing (3)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/v3/services/setSeatsAddOn.server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/webapp/app/v3/services/setSeatsAddOn.server.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: typecheck / typecheck
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: In TypeScript SDK usage, always import from@trigger.dev/sdk, never from@trigger.dev/sdk/v3or use deprecated client.defineJob
Import from@trigger.dev/coresubpaths only, never from the root
Use the Run Engine 2.0 (@internal/run-engine) and redis-worker for all new work, not legacy V1 MarQS queue or deprecated V1 functions
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
apps/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
🧠 Learnings (24)
📓 Common learnings
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3200
File: docs/config/config-file.mdx:353-368
Timestamp: 2026-03-10T12:44:19.869Z
Learning: In the triggerdotdev/trigger.dev repository, docs PRs are often written as companions to implementation PRs (e.g., PR `#3200` documents features being added in PR `#3196`). When reviewing docs PRs, the documented features may exist in a companion/companion PR branch rather than main. Always check companion PRs referenced in the PR description before flagging missing implementations.
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2026-03-10T17:56:26.581Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3201
File: apps/webapp/app/v3/services/setSeatsAddOn.server.ts:25-29
Timestamp: 2026-03-10T17:56:26.581Z
Learning: In the `triggerdotdev/trigger.dev` webapp, service classes such as `SetSeatsAddOnService` and `SetBranchesAddOnService` do NOT need to perform their own userId-to-organizationId authorization checks. Auth is enforced at the route layer: `requireUserId(request)` authenticates the user, and the `_app.orgs.$organizationSlug` layout route enforces that the authenticated user is a member of the org. Any `userId` and `organizationId` reaching these services from org-scoped routes are already validated. This is the consistent pattern used across all org-scoped services in the codebase.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-02-11T16:50:14.167Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx:126-131
Timestamp: 2026-02-11T16:50:14.167Z
Learning: In apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx, MetricsDashboard entities are intentionally scoped to the organization level, not the project level. The dashboard lookup should filter by organizationId only (not projectId), allowing dashboards to be accessed across projects within the same organization. The optional projectId field on MetricsDashboard serves other purposes and should not be used as an authorization constraint.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-03-02T12:43:37.906Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/core/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:37.906Z
Learning: Applies to packages/core/**/*.{ts,tsx,js,jsx} : Never import the root package (trigger.dev/core). Always use subpath imports such as trigger.dev/core/v3, trigger.dev/core/v3/utils, trigger.dev/core/logger, or trigger.dev/core/schemas
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: Applies to **/*.{ts,tsx} : Import from trigger.dev/core subpaths only, never from the root
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-03-02T12:43:48.124Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/trigger-sdk/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:48.124Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx,js,jsx} : Always import from `trigger.dev/sdk`. Never use `trigger.dev/sdk/v3` (deprecated path alias)
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: Applies to apps/webapp/app/v3/**/*.{ts,tsx} : In apps/webapp/app/v3/ directory, only modify V2 code paths - V1/V2 branching should only have V2 modified, as V1 code is legacy
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: Applies to **/*.{ts,tsx} : In TypeScript SDK usage, always import from trigger.dev/sdk, never from trigger.dev/sdk/v3 or use deprecated client.defineJob
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-01-28T16:57:47.620Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 2964
File: apps/webapp/app/components/AskAI.tsx:121-141
Timestamp: 2026-01-28T16:57:47.620Z
Learning: In the trigger.dev webapp codebase, the Button component (apps/webapp/app/components/primitives/Buttons) does not spread unknown props to the underlying <button> element—it only passes specific props (type, disabled, onClick, name, value, ref, form, autoFocus). When using Radix UI's TooltipTrigger with asChild, a span wrapper around the Button is necessary to receive Radix props (aria-describedby, onPointerEnter, onPointerLeave, data-state) while the Button handles its own behavior. Directly making the Button the child of TooltipTrigger with asChild will break accessibility and tooltip functionality.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-02-19T18:09:23.944Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3095
File: apps/webapp/app/components/navigation/DashboardDialogs.tsx:47-47
Timestamp: 2026-02-19T18:09:23.944Z
Learning: In the triggerdotdev/trigger.dev codebase, the pattern `isPaying === false` is used consistently to explicitly check for free plan users. This is a project convention that distinguishes between `isPaying === true` (paying), `isPaying === false` (free), and `isPaying === undefined` (no subscription data). Do not suggest changing this to negation pattern.
```
<!-- <review_comment_addressed>
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-03-02T12:43:02.539Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: docs/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:02.539Z
Learning: Applies to docs/**/*.mdx : Always import from `trigger.dev/sdk` in code examples (never from `trigger.dev/sdk/v3`)
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2025-07-12T18:00:06.163Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2264
File: apps/webapp/app/utils/searchParams.ts:16-18
Timestamp: 2025-07-12T18:00:06.163Z
Learning: The `objectToSearchParams` function in `apps/webapp/app/utils/searchParams.ts` is used to generate URL parameters from objects and is separate from code that parses incoming search parameters. Changes to this function only affect places where it's used to create URLs, not places that parse search parameters from external sources.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
🔇 Additional comments (15)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx (6)
1-63: LGTM!Imports are correctly structured. The
@trigger.dev/core/utilssubpath import fortryCatchaligns with project conventions.
129-166: Authorization is already enforced at the layout layer.The past review flagged a missing auth check here, but per the codebase pattern, the
_app.orgs.$organizationSluglayout route already enforces that the authenticated user (fromrequireUserId) is a member of the organization. This is the consistent pattern used across all org-scoped services. Based on learnings from this repository, service classes such asSetSeatsAddOnServicedo NOT need to perform their own userId-to-organizationId authorization checks.
486-544: LGTM!The cooldown implementation is well-structured with proper cleanup. The initial cooldown calculation from
invite.updatedAtprevents spam after page refresh, and the interval-based countdown with cleanup is correctly implemented.
546-569: LGTM!The accessibility concern from past reviews has been addressed with
aria-label="Revoke invite"on line 560.
571-588: LGTM!The
triggerButtonprop type has been correctly updated toReact.ReactElementto satisfy Radix'sDialogTriggerwithasChild, addressing the previous review concern.
811-834: LGTM!The state logic correctly handles all seat change scenarios with proper boundary checks. The string union return type aligns with the project's preference for string unions over enums.
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx (9)
1-80: LGTM!Imports are well-organized and compliant with coding guidelines. The
@trigger.dev/core/v3subpath import is correctly used.
128-137: LGTM!The discriminated union schema cleanly separates the two action types with appropriate validation constraints.
139-191: LGTM!The purchase-branches action handler properly validates input, handles errors from the service layer, and provides appropriate success/error feedback. The use of
tryCatchand discriminated error handling is clean.
226-529: LGTM!The page component cleanly integrates the new purchase flow with appropriate conditional rendering. The guard
canPurchaseBranches && branchPricingensures the modal only renders when pricing data is available.
531-558: LGTM!The refactored
BranchFilterscomponent correctly usesSearchInputwithresetParamsto handle pagination reset. TheuseCallbackwith an empty dependency array is fine sincesetSearchParamsis stable.
560-634: LGTM!The
UpgradePanelcomponent cleanly handles the branching logic between purchase modal and upgrade dialog based on plan capabilities.
636-866: Overall implementation is solid.The modal correctly handles multiple purchase states (increase, decrease, above_quota, need_to_archive) with appropriate UI feedback and pricing calculations. The summary breakdown is clear and user-friendly.
868-891: LGTM!The state calculation logic correctly handles all edge cases. The ordering of checks ensures
need_to_archiveis checked before returningdecrease, andabove_quotaonly applies to increases.
893-977: LGTM!The
NewBranchPanelcomponent remains unchanged and correctly handles the branch creation flow.
Adds 2 self serve features
1. self serve preview branches
2. self serve team members